This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

201
modules/jdbc-pool/LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

6
modules/jdbc-pool/NOTICE Normal file
View File

@@ -0,0 +1,6 @@
Apache Tomcat JDBC Pool
Copyright 2008-2020 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

View File

@@ -0,0 +1,102 @@
# -----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# build.properties.sample
#
# This is an example "build.properties" file, used to customize building
# Tomcat JDBC Pool for your local environment. It defines the location of all external
# modules that Tomcat JDBC Pool depends on. Copy this file to "build.properties"
# in the top-level source directory, and customize it as needed.
# -----------------------------------------------------------------------------
# ----- Version Control Flags -----
version.major=1
version.minor=1
version.build=0
version.patch=1
version.suffix=
# ----- Default Base Path for Dependent Packages -----
# Please note this path must be absolute, not relative,
# as it is referenced with different working directory
# contexts by the various build scripts.
base.path=${basedir}/includes
compile.source=1.7
compile.target=1.7
compile.release=7
compile.debug=true
# Do not pass -deprecation (-Xlint:deprecation) flag to javac
compile.deprecation=false
# ----- Settings for Junit test database.
# Common settings
testdb.username=root
testdb.password=password
# H2
testdb.url=jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=TRUE;LOCK_TIMEOUT=50000;DEFAULT_LOCK_TIMEOUT=50000
testdb.driverClassName=org.h2.Driver
testdb.validationQuery=SELECT 1
# MySQL
#testdb.url=jdbc:mysql://localhost:3306/mysql?autoReconnect=true
#testdb.driverClassName=com.mysql.jdbc.Driver
#testdb.validationQuery=SELECT 1
# Derby
#testdb.url=jdbc:derby:derbyDB;create=true
#testdb.driverClassName=org.apache.derby.jdbc.EmbeddedDriver
#testdb.validationQuery=VALUES 1
# JUnit Unit Test Suite
junit.version=4.11
junit.home=${base.path}/junit-${junit.version}
junit.jar=${junit.home}/junit-${junit.version}.jar
junit.loc=https://repo.maven.apache.org/maven2/junit/junit/${junit.version}/junit-${junit.version}.jar
# Hamcrest Library, used by JUnit
hamcrest.version=1.3
hamcrest.home=${base.path}/hamcrest-${hamcrest.version}
hamcrest.jar=${hamcrest.home}/hamcrest-core-${hamcrest.version}.jar
hamcrest.loc=https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/${hamcrest.version}/hamcrest-core-${hamcrest.version}.jar
mysql.home=${base.path}/mysql-connector-java-5.1.12
mysql.jar=${mysql.home}/mysql-connector-java-5.1.12-bin.jar
mysql.loc=http://mysql.mirrors.hoobly.com/Downloads/Connector-J/mysql-connector-java-5.1.12.zip
tomcat.version=8.0.14
tomcat.home=${base.path}/apache-tomcat-${tomcat.version}
tomcat.dbcp.jar=${tomcat.home}/lib/tomcat-dbcp.jar
tomcat.juli.jar=${tomcat.home}/bin/tomcat-juli.jar
tomcat.loc=https://archive.apache.org/dist/tomcat/tomcat-8/v${tomcat.version}/bin/apache-tomcat-${tomcat.version}.zip
tomcat.project.loc=https://svn.apache.org/repos/asf/tomcat/trunk/webapps/docs/project.xml
tomcat.project.dest=${base.path}/project.xml
tomcat.xsl.loc=https://svn.apache.org/repos/asf/tomcat/trunk/webapps/docs/tomcat-docs.xsl
tomcat.xsl.dest=${base.path}/tomcat-docs.xsl
derby.home=${base.path}/db-derby-10.5.1.1-bin
derby.loc=https://archive.apache.org/dist/db/derby/db-derby-10.5.1.1/db-derby-10.5.1.1-bin.tar.gz
derby.jar=${derby.home}/lib/derby.jar
h2.version=1.2.129
h2.home=${base.path}/h2-${h2.version}
h2.loc=https://repo.maven.apache.org/maven2/com/h2database/h2/1.2.129/h2-1.2.129.jar
h2.jar=${h2.home}/h2-1.2.129.jar

529
modules/jdbc-pool/build.xml Normal file
View File

@@ -0,0 +1,529 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Basedir points at tomcat-trunk directory -->
<project name="Tomcat JDBC Pool" default="simplebuild" basedir=".">
<!-- ===================== Initialize Property Values =================== -->
<!-- We read customizable properties from the file "build.properties.default" -->
<!-- and also from "build.properties" if it exists. -->
<!-- The values in "build.properties" have stronger preference. -->
<!-- If you want to customize your build, you can either change the values -->
<!-- directly in the default file, or create a new build.properties and set -->
<!-- the values there. This way you don't have to change a file which is part -->
<!-- of the original project source code. -->
<!-- See "build.properties.default" for some property values you may -->
<!-- customize. -->
<property file="${user.home}/build.properties"/>
<property file="${basedir}/build.properties"/>
<property file="${basedir}/build.properties.default"/>
<property name="version" value="${version.major}.${version.minor}.${version.build}.${version.patch}${version.suffix}" />
<property name="version.major.minor" value="${version.major}.${version.minor}" />
<!-- Project Properties -->
<property name="name" value="Apache Tomcat JDBC Pool" />
<tstamp>
<format property="year" pattern="yyyy" locale="en" timezone="UTC" />
<format property="TODAY" pattern="MMM d yyyy" locale="en" timezone="UTC" />
<format property="TSTAMP" pattern="HH:mm:ss" locale="en" timezone="UTC" />
</tstamp>
<property name="project" value="apache-tomcat-jdbc-pool" />
<property name="final.name" value="${project}-${version}" />
<property name="final-src.name" value="${project}-${version}-src" />
<!-- Build Defaults -->
<property name="tomcat.pool" value="${basedir}/./output"/>
<property name="tomcat.build" value="${tomcat.pool}/build"/>
<property name="tomcat.classes" value="${tomcat.pool}/classes"/>
<property name="tomcat.api" value="${tomcat.pool}/api"/>
<property name="tomcat.testclasses" value="${tomcat.pool}/testclasses"/>
<!-- JAR Artifacts -->
<property name="tomcat-jdbc.jar" value="${tomcat.pool}/tomcat-jdbc.jar"/>
<property name="tomcat-jdbc-src.jar" value="${tomcat.pool}/tomcat-jdbc-src.jar"/>
<property name="tomcat-jdbc-test.jar" value="${tomcat.pool}/tomcat-jdbc-test.jar"/>
<property name="tomcat-jdbc-test-src.jar" value="${tomcat.pool}/tomcat-jdbc-test-src.jar"/>
<property name="tomcat-jdbc-api.jar" value="${tomcat.pool}/tomcat-jdbc-api.jar"/>
<!-- Classpath -->
<path id="tomcat.jdbc.classpath">
<pathelement location="${tomcat.classes}"/>
<pathelement location="${tomcat.juli.jar}"/>
</path>
<path id="test.classpath">
<pathelement location="${tomcat-jdbc.jar}"/>
<pathelement location="${tomcat.testclasses}"/>
<pathelement location="${c3p0.jar}"/>
<pathelement location="${mysql.jar}"/>
<pathelement location="${junit.jar}"/>
<pathelement location="${hamcrest.jar}"/>
<pathelement location="${tomcat.dbcp.jar}"/>
<pathelement location="${derby.jar}"/>
<pathelement location="${h2.jar}"/>
</path>
<fileset id="license.notice" dir="${basedir}">
<include name="NOTICE"/>
<include name="LICENSE"/>
</fileset>
<!-- Version info filter set -->
<filterset id="version.filters">
<filter token="YEAR" value="${year}"/>
<filter token="VERSION" value="${version}"/>
<filter token="VERSION_MAJOR" value="${version.major}"/>
<filter token="VERSION_MAJOR_MINOR" value="${version.major.minor}"/>
<filter token="VERSION_BUILT" value="${TODAY} ${TSTAMP} UTC"/>
<filter token="TOMCAT_PROJECT_DEST" value="${tomcat.project.dest}"/>
</filterset>
<target name="prepare">
<mkdir dir="${tomcat.build}"/>
<mkdir dir="${tomcat.classes}"/>
<mkdir dir="${tomcat.testclasses}"/>
<copy todir="${tomcat.pool}">
<fileset dir="${basedir}">
<include name="LICENSE"/>
<include name="NOTICE"/>
<include name="build.xml"/>
<include name="doc/*.xml"/>
<include name="resources/MANIFEST.MF"/>
</fileset>
<filterset refid="version.filters"/>
</copy>
</target>
<target name="javadoc" depends="prepare">
<xslt basedir="${tomcat.pool}/doc"
destdir="${basedir}/src/main/java/org/apache/tomcat/jdbc/pool"
extension=".html"
style="${basedir}/doc/package.xsl"
excludes="build.xml"
includes="jdbc-pool.xml">
<param name="relative-path" expression="https://tomcat.apache.org/tomcat-8.5-doc"/>
<param name="apache-logo" expression="/images/asf-logo.svg"/>
</xslt>
<move tofile="${basedir}/src/main/java/org/apache/tomcat/jdbc/pool/package.html"
file="${basedir}/src/main/java/org/apache/tomcat/jdbc/pool/jdbc-pool.html"/>
<javadoc
destdir="${tomcat.api}"
bottom="Copyright &amp;#169; 2000-${year} Apache Software Foundation. All Rights Reserved."
encoding="UTF-8"
docencoding="UTF-8"
charset="UTF-8"
additionalparam="-breakiterator -notimestamp"
verbose="false">
<classpath refid="tomcat.jdbc.classpath"/>
<link href="http://docs.oracle.com/javase/7/docs/api/"/>
<sourcepath location="${basedir}/src/main/java"/>
</javadoc>
<!-- connection pool API file-->
<jar jarfile="${tomcat-jdbc-api.jar}" update="true">
<fileset dir="${tomcat.api}"/>
<fileset refid="license.notice"/>
</jar>
<delete file="${basedir}/src/main/java/org/apache/tomcat/jdbc/pool/package.html"/>
</target>
<target name="build" depends="prepare,download,build-src">
<!-- compile org.apache.tomcat.jdbc-->
<javac srcdir="${basedir}/src/main/java" destdir="${tomcat.classes}"
debug="${compile.debug}"
deprecation="${compile.deprecation}"
source="${compile.source}"
target="${compile.target}"
release="${compile.release}"
encoding="ISO-8859-1"
includeantruntime="false">
<classpath refid="tomcat.jdbc.classpath"/>
<include name="org/apache/tomcat/jdbc/**" />
</javac>
<!-- connection pool JAR File -->
<jar jarfile="${tomcat-jdbc.jar}" update="true" manifest="${tomcat.pool}/resources/MANIFEST.MF">
<fileset dir="${tomcat.classes}">
<include name="org/apache/tomcat/jdbc/**" />
</fileset>
<fileset dir="${basedir}/src/main/java">
<include name="org/apache/tomcat/jdbc/**/*.xml" />
</fileset>
<fileset refid="license.notice"/>
</jar>
</target>
<target name="build-src">
<!-- connection pool source file-->
<jar jarfile="${tomcat-jdbc-src.jar}" update="true">
<fileset dir="${basedir}/src/main/java">
<include name="org/apache/tomcat/jdbc/**" />
</fileset>
<fileset refid="license.notice"/>
</jar>
</target>
<target name="build-test" depends="prepare, build, download-test">
<mkdir dir="${tomcat.pool}"/>
<!-- compile org.apache.tomcat.jdbc-->
<javac srcdir="${basedir}/src/test/java" destdir="${tomcat.testclasses}"
debug="${compile.debug}"
deprecation="${compile.deprecation}"
source="${compile.source}"
target="${compile.target}"
release="${compile.release}"
encoding="ISO-8859-1"
includeantruntime="false">
<classpath refid="tomcat.jdbc.classpath"/>
<classpath refid="test.classpath"/>
<include name="org/apache/tomcat/jdbc/**" />
</javac>
<!-- connection pool JAR File -->
<jar jarfile="${tomcat-jdbc-test.jar}" update="true">
<fileset dir="${tomcat.testclasses}">
<include name="org/apache/tomcat/jdbc/**" />
</fileset>
<fileset dir="${basedir}/src/test/java">
<include name="org/apache/tomcat/jdbc/**/*.xml" />
</fileset>
<fileset refid="license.notice"/>
</jar>
<jar jarfile="${tomcat-jdbc-test-src.jar}" update="true">
<fileset dir="${basedir}/src/test/java">
<include name="org/apache/tomcat/jdbc/**" />
</fileset>
<fileset refid="license.notice"/>
</jar>
</target>
<target name="clean">
<mkdir dir="${tomcat.pool}"/>
<delete file="${tomcat-jdbc.jar}" />
<delete file="${tomcat-jdbc-src.jar}"/>
<delete includeemptydirs="true" failonerror="false">
<fileset dir="${tomcat.classes}">
<include name="org/apache/tomcat/jdbc/**"/>
</fileset>
<fileset dir="${tomcat.pool}">
<include name="*.html"/>
<include name="LICENSE"/>
<include name="NOTICE"/>
</fileset>
</delete>
<delete dir="${tomcat.pool}/doc"/>
<delete dir="${tomcat.classes}"/>
<delete dir="${tomcat.testclasses}"/>
<delete file="${tomcat.pool}/build.xml"/>
<delete dir="${tomcat.pool}/build"/>
<delete dir="${tomcat.api}"/>
</target>
<target name="docs" depends="prepare">
<xslt basedir="${tomcat.pool}/doc"
destdir="${tomcat.pool}/"
extension=".html"
style="${tomcat.xsl.dest}"
excludes="build.xml project.xml"
includes="*.xml">
<param name="relative-path" expression="https://tomcat.apache.org/tomcat-8.5-doc"/>
<param name="apache-logo" expression="/images/asf-logo.svg"/>
</xslt>
</target>
<target name="dist" depends="clean, build, build-test, docs, javadoc">
<property name="destdir" value="${tomcat.pool}/release/v${version}"/>
<mkdir dir="${destdir}"/>
<zip destfile="${destdir}/apache-tomcat-jdbc-${version}-bin.zip">
<fileset dir="${tomcat.pool}">
<include name="jdbc-pool.html"/>
<include name="changelog.html"/>
<include name="LICENSE"/>
<include name="NOTICE"/>
<include name="tomcat-jdbc.jar"/>
<include name="tomcat-jdbc-test.jar"/>
<include name="tomcat-jdbc-src.jar"/>
<include name="tomcat-jdbc-test-src.jar"/>
</fileset>
<fileset dir="${tomcat.home}/bin">
<include name="tomcat-juli.jar"/>
</fileset>
</zip>
<tar destfile="${destdir}/apache-tomcat-jdbc-${version}-bin.tar">
<fileset dir="${tomcat.pool}">
<include name="jdbc-pool.html"/>
<include name="changelog.html"/>
<include name="LICENSE"/>
<include name="NOTICE"/>
<include name="tomcat-jdbc.jar"/>
<include name="tomcat-jdbc-test.jar"/>
<include name="tomcat-jdbc-src.jar"/>
<include name="tomcat-jdbc-test-src.jar"/>
</fileset>
<fileset dir="${tomcat.home}/bin">
<include name="tomcat-juli.jar"/>
</fileset>
</tar>
<gzip src="${destdir}/apache-tomcat-jdbc-${version}-bin.tar"
destfile="${destdir}/apache-tomcat-jdbc-${version}-bin.tar.gz"/>
<delete file="${destdir}/apache-tomcat-jdbc-${version}-bin.tar"/>
<zip destfile="${destdir}/apache-tomcat-jdbc-${version}-src.zip">
<zipfileset src="${tomcat-jdbc-src.jar}" prefix="java"/>
<zipfileset src="${tomcat-jdbc-test-src.jar}" prefix="test"/>
<fileset dir="${basedir}">
<include name="build.xml"/>
<include name="build.properties.default"/>
<include name="doc/*.xml"/>
<include name="doc/*.xsl"/>
<include name="resources/MANIFEST.MF"/>
<include name="LICENSE"/>
<include name="NOTICE"/>
</fileset>
</zip>
<tar destfile="${destdir}/apache-tomcat-jdbc-${version}-src.tar">
<zipfileset src="${tomcat-jdbc-src.jar}" prefix="java"/>
<zipfileset src="${tomcat-jdbc-test-src.jar}" prefix="test"/>
<fileset dir="${basedir}">
<include name="build.xml"/>
<include name="build.properties.default"/>
<include name="doc/*.xml"/>
<include name="doc/*.xsl"/>
<include name="resources/MANIFEST.MF"/>
<include name="LICENSE"/>
<include name="NOTICE"/>
</fileset>
</tar>
<gzip src="${destdir}/apache-tomcat-jdbc-${version}-src.tar"
destfile="${destdir}/apache-tomcat-jdbc-${version}-src.tar.gz"/>
<delete file="${destdir}/apache-tomcat-jdbc-${version}-src.tar"/>
<echo message="Options +Indexes" file="${destdir}/.htaccess"/>
<checksum file="${destdir}/apache-tomcat-jdbc-${version}-bin.tar.gz" forceOverwrite="yes" fileext=".md5" />
<checksum file="${destdir}/apache-tomcat-jdbc-${version}-bin.zip" forceOverwrite="yes" fileext=".md5" />
<checksum file="${destdir}/apache-tomcat-jdbc-${version}-src.tar.gz" forceOverwrite="yes" fileext=".md5" />
<checksum file="${destdir}/apache-tomcat-jdbc-${version}-src.zip" forceOverwrite="yes" fileext=".md5" />
</target>
<!-- Download and dependency building -->
<target name="proxyflags">
<!-- check proxy parameters. -->
<condition property="useproxy">
<equals arg1="${proxy.use}" arg2="on" />
</condition>
</target>
<target name="setproxy" depends="proxyflags" if="useproxy">
<taskdef name="setproxy"
classname="org.apache.tools.ant.taskdefs.optional.net.SetProxy" />
<setproxy proxyhost="${proxy.host}" proxyport="${proxy.port}"
proxyuser="${proxy.user}" proxypassword="${proxy.password}" />
<echo message="Using ${proxy.host}:${proxy.port} to download ${sourcefile}"/>
</target>
<target name="testexist">
<echo message="Testing for ${destfile}"/>
<available file="${destfile}" property="exist"/>
</target>
<target name="downloadfile" unless="exist" depends="setproxy,testexist">
<!-- Download extract the file -->
<mkdir dir="${destdir}" />
<get src="${sourcefile}" dest="${destfile}" />
</target>
<target name="downloadgz" unless="exist" depends="setproxy,testexist">
<!-- Download and extract the package -->
<get src="${sourcefile}" dest="${base.path}/file.tar.gz" />
<gunzip src="${base.path}/file.tar.gz" dest="${base.path}/file.tar"/>
<untar src="${base.path}/file.tar" dest="${base.path}"/>
<delete file="${base.path}/file.tar"/>
<delete file="${base.path}/file.tar.gz"/>
</target>
<target name="downloadzip" unless="exist" depends="setproxy,testexist">
<!-- Download and extract the package -->
<get src="${sourcefile}" dest="${base.path}/file.zip" />
<mkdir dir="${destdir}" />
<unzip src="${base.path}/file.zip" dest="${destdir}"/>
<delete file="${base.path}/file.zip"/>
</target>
<target name="download" unless="skip.download">
<mkdir dir="${base.path}"/>
<!--
<antcall target="downloadzip">
<param name="sourcefile" value="${dbcp.loc}"/>
<param name="destfile" value="${dbcp.jar}"/>
<param name="destdir" value="${base.path}"/>
</antcall>
-->
<antcall target="downloadzip">
<param name="sourcefile" value="${tomcat.loc}"/>
<param name="destfile" value="${tomcat.juli.jar}"/>
<param name="destdir" value="${base.path}"/>
</antcall>
<antcall target="downloadfile">
<param name="sourcefile" value="${tomcat.xsl.loc}"/>
<param name="destfile" value="${tomcat.xsl.dest}"/>
<param name="destdir" value="${base.path}"/>
</antcall>
<antcall target="downloadfile">
<param name="sourcefile" value="${tomcat.project.loc}"/>
<param name="destfile" value="${tomcat.project.dest}"/>
<param name="destdir" value="${base.path}"/>
</antcall>
<!-- Derby database
<antcall target="downloadgz">
<param name="sourcefile" value="${derby.loc}"/>
<param name="destfile" value="${derby.jar}"/>
<param name="destdir" value="${base.path}"/>
</antcall>
-->
<antcall target="downloadfile">
<param name="sourcefile" value="${h2.loc}"/>
<param name="destfile" value="${h2.jar}"/>
<param name="destdir" value="${h2.home}"/>
</antcall>
<antcall target="downloadfile">
<param name="sourcefile" value="${junit.loc}"/>
<param name="destfile" value="${junit.jar}"/>
<param name="destdir" value="${junit.home}"/>
</antcall>
<antcall target="downloadfile">
<param name="sourcefile" value="${hamcrest.loc}"/>
<param name="destfile" value="${hamcrest.jar}"/>
<param name="destdir" value="${hamcrest.home}"/>
</antcall>
</target>
<target name="download-test" depends="prepare">
<mkdir dir="${base.path}"/>
<!-- Derby database
<antcall target="downloadgz">
<param name="sourcefile" value="${derby.loc}"/>
<param name="destfile" value="${derby.jar}"/>
<param name="destdir" value="${base.path}"/>
</antcall>
-->
<antcall target="downloadfile">
<param name="sourcefile" value="${h2.loc}"/>
<param name="destfile" value="${h2.jar}"/>
<param name="destdir" value="${h2.home}"/>
</antcall>
<antcall target="downloadfile">
<param name="sourcefile" value="${junit.loc}"/>
<param name="destfile" value="${junit.jar}"/>
<param name="destdir" value="${junit.home}"/>
</antcall>
<antcall target="downloadfile">
<param name="sourcefile" value="${hamcrest.loc}"/>
<param name="destfile" value="${hamcrest.jar}"/>
<param name="destdir" value="${hamcrest.home}"/>
</antcall>
</target>
<!-- Extract the testdb.* properties and remove the leading testdb. -->
<propertyset dynamic="false" id="testdb">
<propertyref prefix="testdb"/>
<mapper type="regexp" from="^testdb\.(.*)$$" to="\1"/>
</propertyset>
<target name="test" depends="build,build-test">
<echo/>
<echo>Creating test table for test purposes.</echo>
<junit printsummary="withOutAndErr" showoutput="on">
<syspropertyset refid="testdb"/>
<formatter type="brief" usefile="no"/>
<classpath refid="tomcat.jdbc.classpath"/>
<classpath refid="test.classpath"/>
<batchtest fork="yes" todir="${tomcat.testclasses}">
<fileset dir="${basedir}/src/test/java">
<include name="**/CreateTestTable*"/>
</fileset>
</batchtest>
</junit>
<echo/>
<echo>Performance and fairness tests.</echo>
<junit printsummary="withOutAndErr" showoutput="on">
<syspropertyset refid="testdb"/>
<formatter type="brief" usefile="no"/>
<classpath refid="tomcat.jdbc.classpath"/>
<classpath refid="test.classpath"/>
<batchtest fork="yes" todir="${tomcat.testclasses}">
<fileset dir="${basedir}/src/test/java">
<include name="org/apache/tomcat/jdbc/test/*CheckOutThreadTest*.java"/>
<include name="org/apache/tomcat/jdbc/test/FairnessTest*.java"/>
<exclude name="**/CreateTestTable*"/>
<exclude name="**/DefaultTestCase*"/>
</fileset>
</batchtest>
</junit>
<echo/>
<echo>Functional tests.</echo>
<junit printsummary="yes">
<syspropertyset refid="testdb"/>
<formatter type="brief" usefile="no"/>
<classpath refid="tomcat.jdbc.classpath"/>
<classpath refid="test.classpath"/>
<batchtest fork="yes" todir="${tomcat.testclasses}">
<fileset dir="${basedir}/src/test/java">
<include name="org/apache/tomcat/jdbc/test/*Test*.java"/>
<exclude name="org/apache/tomcat/jdbc/test/*CheckOutThreadTest*.java"/>
<exclude name="**/CreateTestTable*"/>
<exclude name="**/DefaultTestCase*"/>
<exclude name="**/CheckOutThread*"/>
<exclude name="**/Fairness*"/>
</fileset>
</batchtest>
</junit>
</target>
<target name="simplebuild" depends="build"/>
<target name="clean-and-build" depends="clean,build"/>
<target name="onetest" depends="build,build-test">
<echo>Testing:${test}</echo>
<junit printsummary="withOutAndErr" showoutput="on">
<syspropertyset refid="testdb"/>
<formatter type="brief" usefile="no"/>
<classpath refid="tomcat.jdbc.classpath"/>
<classpath refid="test.classpath"/>
<batchtest fork="yes" todir="${tomcat.testclasses}">
<fileset dir="${basedir}/src/test/java">
<include name="org/apache/tomcat/jdbc/test/*${test}*.java"/>
<exclude name="**/CreateTestTable*"/>
<exclude name="**/DefaultTestCase*"/>
</fileset>
</batchtest>
</junit>
</target>
</project>

View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE document [
<!ENTITY project SYSTEM "@TOMCAT_PROJECT_DEST@">
]>
<?xml-stylesheet type="text/xsl" href="package.xsl"?>
<document url="changelog.html">
&project;
<properties>
<author email="fhanik@apache.org">Filip Hanik</author>
<title>Changelog</title>
</properties>
<body>
<section name="Tomcat JDBC Connection Pool - Apache Tomcat 7.0.19 and later">
<p>
Starting with Apache Tomcat 7.0.19 in July 2011, Tomcat JDBC Connection Pool
is built and released as a component in official releases of Tomcat.
The changes are now listed in "jdbc-pool" sections of Apache Tomcat
changelog file. This changelog file is obsolete.
</p>
</section>
<section name="Tomcat JDBC Connection Pool 1.1.0.0">
<subsection name="pool">
<changelog>
<add><rev>1207712</rev> Pool cleaner should be a global thread, not spawn one thread per connection pool. (fhanik)</add>
<fix><rev>1073531</rev> <bug>50805</bug> Only initialize connections once when async (fhanik)</fix>
<fix><rev>1076380</rev> <bug>50857</bug> Correctly handle timeouts when the pool is busy when async (fhanik)</fix>
<add>Added QueryTimeoutInterceptor to be able to configure timeouts on running queries automatically.</add>
</changelog>
</subsection>
</section>
<section name="Tomcat JDBC Connection Pool 1.0.9.4">
<subsection name="pool">
<changelog>
<fix><rev>1069864</rev> <bug>50759</bug> Correctly set validation timestamp when using external validator.(fhanik)</fix>
</changelog>
</subsection>
</section>
<section name="Tomcat JDBC Connection Pool 1.0.9.3">
<subsection name="pool">
<changelog>
<fix><rev>1060998</rev> <bug>50613</bug> Fix concurrency issue around pool size calculation.(fhanik)</fix>
</changelog>
</subsection>
</section>
<section name="Tomcat JDBC Connection Pool 1.0.9.2">
<subsection name="pool">
<changelog>
<fix><rev>1057743</rev> Make sure passwords are masked.(fhanik)</fix>
</changelog>
</subsection>
</section>
<section name="Tomcat JDBC Connection Pool 1.0.9.0">
<subsection name="pool">
<changelog>
<fix><rev>997321</rev> Ensure threads borrowing connections do not
get stuck waiting for a new connection if a connection is released in
another thread. (markt)</fix>
<fix><rev>995432</rev> Make interceptor class names, property names
and property values tolerant of whitespace by trimming the values before
use. (markt)</fix>
<fix><rev>995091</rev> <bug>49831</bug> Make sure pooled XAConnections are
closed when the connection pool shuts down. Patch provided by Daniel
Mikusa. (markt)</fix>
<update><rev>995087</rev> Code clean-up. Remove some unused code. (markt)
</update>
<update><rev>995083</rev> Update to Tomcat 6.0.29 (for JULI). (markt)
</update>
<update><rev>992409</rev> Code clean-up. Reduce sequences of three or more
blank lines to two blank lines. (markt)</update>
<add><rev>952811</rev>, <rev>995095</rev> <bug>48814</bug> Add Validator
interface and allow users to configure a Validator class name. Patch
provided by Matt Passell. (markt)</add>
<update><rev>948073</rev> Code clean-up. Remove unused imports. (markt)
</update>
<fix><rev>943434</rev> <bug>49224</bug> Only try setting the username and
password if they are non-null. Patch provided by Matt Passell. (markt)
</fix>
<fix><rev>943032</rev> <bug>49269</bug> Set maxIdle to maxActive by
default to prevent warning on start when maxIdle > maxActive. Patch
provided by Matt Passell. (markt)</fix>
<fix><rev>940574</rev> <bug>49241</bug> Don&apos;t ignore the
suspectTimeout property. (fhanik)</fix>
<fix><rev>939320</rev> Fix svn:keywords for property replacement.
(kkolinko)</fix>
<add><rev>931550</rev>, <rev>934651</rev>, <rev>934677</rev> Add a
statement cache. (fhanik)</add>
<update><rev>919076</rev> Improve XA support. (fhanik)</update>
<fix><rev>915940</rev> <bug>48392</bug> Add an interceptor to wrap
Statements and ResultSets to prevent access to the physical connection.
(fhanik)</fix>
<fix><rev>912026</rev> Call <code>setTransactionIsolation()</code> before
anything else as some drivers require this to be the first call. (fhanik)
</fix>
<update><rev>900017</rev> Update Javadoc for XADataSource. (kkolinko)
</update>
</changelog>
</subsection>
</section>
<section name="Tomcat JDBC Connection Pool prior to 1.0.9.0 (incomplete)">
<subsection name="pool">
<changelog>
<update><rev>720253</rev> Document how to use interceptors</update>
<update><rev>717972</rev> Added an interceptor that will clean up non closed statements when a connection is returned to the pool. (<code>org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer</code>)</update>
<update><rev>713763</rev> Improve connection state handling</update>
<fix><rev>713763</rev> Improve connection state handling</fix>
</changelog>
</subsection>
</section>
</body>
</document>

View File

@@ -0,0 +1,988 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE document [
<!ENTITY project SYSTEM "@TOMCAT_PROJECT_DEST@">
]>
<document url="jdbc-pool.html">
&project;
<properties>
<author email="fhanik@apache.org">Filip Hanik</author>
<title>The Tomcat JDBC Connection Pool</title>
</properties>
<body>
<section name="Table of Contents">
<toc/>
</section>
<section name="Introduction">
<p>The <strong>JDBC Connection Pool <code>org.apache.tomcat.jdbc.pool</code></strong>
is a replacement or an alternative to the <a href="https://commons.apache.org/dbcp/">Apache Commons DBCP</a>
connection pool.</p>
<p>So why do we need a new connection pool?</p>
<p>Here are a few of the reasons:</p>
<ol>
<li>Commons DBCP 1.x is single threaded. In order to be thread safe
Commons locks the entire pool for short periods during both object
allocation and object return. Note that this does not apply to
Commons DBCP 2.x.</li>
<li>Commons DBCP 1.x can be slow. As the number of logical CPUs grows and
the number of concurrent threads attempting to borrow or return
objects increases, the performance suffers. For highly concurrent
systems the impact can be significant. Note that this does not apply
to Commons DBCP 2.x.</li>
<li>Commons DBCP is over 60 classes. tomcat-jdbc-pool core is 8 classes,
hence modifications for future requirement will require much less
changes. This is all you need to run the connection pool itself, the
rest is gravy.</li>
<li>Commons DBCP uses static interfaces. This means you have to use the
right version for a given JRE version or you may see
<code>NoSuchMethodException</code> exceptions.</li>
<li>It's not worth rewriting over 60 classes, when a connection pool can
be accomplished with a much simpler implementation.</li>
<li>Tomcat jdbc pool implements the ability retrieve a connection
asynchronously, without adding additional threads to the library
itself.</li>
<li>Tomcat jdbc pool is a Tomcat module, it depends on Tomcat JULI, a
simplified logging framework used in Tomcat.</li>
<li>Retrieve the underlying connection using the
<code>javax.sql.PooledConnection</code> interface.</li>
<li>Starvation proof. If a pool is empty, and threads are waiting for a
connection, when a connection is returned, the pool will awake the
correct thread waiting. Most pools will simply starve.</li>
</ol>
<p>Features added over other connection pool implementations</p>
<ol>
<li>Support for highly concurrent environments and multi core/cpu systems.</li>
<li>Dynamic implementation of interface, will support <code>java.sql</code> and <code>javax.sql</code> interfaces for
your runtime environment (as long as your JDBC driver does the same), even when compiled with a lower version of the JDK.</li>
<li>Validation intervals - we don't have to validate every single time we use the connection, we can do this
when we borrow or return the connection, just not more frequent than an interval we can configure.</li>
<li>Run-Once query, a configurable query that will be run only once, when the connection to the database is established.
Very useful to setup session settings, that you want to exist during the entire time the connection is established.</li>
<li>Ability to configure custom interceptors.
This allows you to write custom interceptors to enhance the functionality. You can use interceptors to gather query stats,
cache session states, reconnect the connection upon failures, retry queries, cache query results, and so on.
Your options are endless and the interceptors are dynamic, not tied to a JDK version of a
<code>java.sql</code>/<code>javax.sql</code> interface.</li>
<li>High performance - we will show some differences in performance later on</li>
<li>Extremely simple, due to the very simplified implementation, the line count and source file count are very low, compare with c3p0
that has over 200 source files(last time we checked), Tomcat jdbc has a core of 8 files, the connection pool itself is about half
that. As bugs may occur, they will be faster to track down, and easier to fix. Complexity reduction has been a focus from inception.</li>
<li>Asynchronous connection retrieval - you can queue your request for a connection and receive a <code>Future&lt;Connection&gt;</code> back.</li>
<li>Better idle connection handling. Instead of closing connections directly, it can still pool connections and sizes the idle pool with a smarter algorithm.</li>
<li>You can decide at what moment connections are considered abandoned, is it when the pool is full, or directly at a timeout
by specifying a pool usage threshold.
</li>
<li>The abandon connection timer will reset upon a statement/query activity. Allowing a connections that is in use for a long time to not timeout.
This is achieved using the <code>ResetAbandonedTimer</code>
</li>
<li>Close connections after they have been connected for a certain time. Age based close upon return to the pool.
</li>
<li>Get JMX notifications and log entries when connections are suspected for being abandoned. This is similar to
the <code>removeAbandonedTimeout</code> but it doesn't take any action, only reports the information.
This is achieved using the <code>suspectTimeout</code> attribute.</li>
<li>Connections can be retrieved from a <code>java.sql.Driver</code>, <code>javax.sql.DataSource</code> or <code>javax.sql.XADataSource</code>
This is achieved using the <code>dataSource</code> and <code>dataSourceJNDI</code> attributes.</li>
<li>XA connection support</li>
</ol>
</section>
<section name="How to use">
<p>
Usage of the Tomcat connection pool has been made to be as simple as possible, for those of you that are familiar with commons-dbcp, the
transition will be very simple. Moving from other connection pools is also fairly straight forward.
</p>
<subsection name="Additional features">
<p>The Tomcat connection pool offers a few additional features over what most other pools let you do:</p>
<ul>
<li><code>initSQL</code> - the ability to run an SQL statement exactly once, when the connection is created</li>
<li><code>validationInterval</code> - in addition to running validations on connections, avoid running them too frequently.</li>
<li><code>jdbcInterceptors</code> - flexible and pluggable interceptors to create any customizations around the pool,
the query execution and the result set handling. More on this in the advanced section.</li>
<li><code>fairQueue</code> - Set the fair flag to true to achieve thread fairness or to use asynchronous connection retrieval</li>
</ul>
</subsection>
<subsection name="Inside the Apache Tomcat Container">
<p>
The Tomcat Connection pool is configured as a resource described in <a href="http://tomcat.apache.org/tomcat-8.5-doc/jndi-datasource-examples-howto.html" target="_blank">The Tomcat JDBC documentation</a>
With the only difference being that you have to specify the <code>factory</code> attribute and set the value to
<code>org.apache.tomcat.jdbc.pool.DataSourceFactory</code>
</p>
</subsection>
<subsection name="Standalone">
<p>
The connection pool only has another dependency, and that is on tomcat-juli.jar.
To configure the pool in a stand alone project using bean instantiation, the bean to instantiate is
<code>org.apache.tomcat.jdbc.pool.DataSource</code>. The same attributes (documented below) as you use to configure a connection
pool as a JNDI resource, are used to configure a data source as a bean.
</p>
</subsection>
<subsection name="JMX">
<p>
The connection pool object exposes an MBean that can be registered.
In order for the connection pool object to create the MBean, the flag <code>jmxEnabled</code> has to be set to true.
This doesn't imply that the pool will be registered with an MBean server, merely that the MBean is created.
In a container like Tomcat, Tomcat itself registers the DataSource with the MBean server, the
<code>org.apache.tomcat.jdbc.pool.DataSource</code> object will then register the actual
connection pool MBean.
If you're running outside of a container, you can register the DataSource yourself under any object name you specify,
and it propagates the registration to the underlying pool. To do this you would call <code>mBeanServer.registerMBean(dataSource.getPool().getJmxPool(),objectname)</code>.
Prior to this call, ensure that the pool has been created by calling <code>dataSource.createPool()</code>.
</p>
</subsection>
</section>
<section name="Attributes">
<p>To provide a very simple switch to and from commons-dbcp and tomcat-jdbc-pool,
Most attributes are the same and have the same meaning.</p>
<subsection name="JNDI Factory and Type">
<attributes>
<attribute name="factory" required="true">
<p>factory is required, and the value should be <code>org.apache.tomcat.jdbc.pool.DataSourceFactory</code></p>
</attribute>
<attribute name="type" required="true">
<p>Type should always be <code>javax.sql.DataSource</code> or <code>javax.sql.XADataSource</code></p>
<p>Depending on the type a <code>org.apache.tomcat.jdbc.pool.DataSource</code> or a <code>org.apache.tomcat.jdbc.pool.XADataSource</code> will be created.</p>
</attribute>
</attributes>
</subsection>
<subsection name="System Properties">
<p>System properties are JVM wide, affect all pools created in the JVM</p>
<attributes>
<attribute name="org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader" required="false">
<p>(boolean) Controls classloading of dynamic classes, such as
JDBC drivers, interceptors and validators. If set to
<code>false</code>, default value, the pool will first attempt
to load using the current loader (i.e. the class loader that
loaded the pool classes) and if class loading fails attempt to
load using the thread context loader. Set this value to
<code>true</code>, if you wish to remain backwards compatible
with Apache Tomcat 8.0.8 and earlier, and only attempt the
current loader.
If not set then the default value is <code>false</code>.
</p>
</attribute>
</attributes>
</subsection>
<subsection name="Common Attributes">
<p>These attributes are shared between commons-dbcp and tomcat-jdbc-pool, in some cases default values are different.</p>
<attributes>
<attribute name="defaultAutoCommit" required="false">
<p>(boolean) The default auto-commit state of connections created by this pool. If not set, default is JDBC driver default (If not set then the <code>setAutoCommit</code> method will not be called.)</p>
</attribute>
<attribute name="defaultReadOnly" required="false">
<p>(boolean) The default read-only state of connections created by this pool. If not set then the <code>setReadOnly</code> method will not be called. (Some drivers don't support read only mode, ex: Informix)</p>
</attribute>
<attribute name="defaultTransactionIsolation" required="false">
<p>(String) The default TransactionIsolation state of connections created by this pool. One of the following: (see javadoc )</p>
<ul>
<li><code>NONE</code></li>
<li><code>READ_COMMITTED</code></li>
<li><code>READ_UNCOMMITTED</code></li>
<li><code>REPEATABLE_READ</code></li>
<li><code>SERIALIZABLE</code></li>
</ul>
<p>If not set, the method will not be called and it defaults to the JDBC driver.</p>
</attribute>
<attribute name="defaultCatalog" required="false">
<p>(String) The default catalog of connections created by this pool.</p>
</attribute>
<attribute name="driverClassName" required="true">
<p>(String) The fully qualified Java class name of the JDBC driver to be used. The driver has to be accessible
from the same classloader as tomcat-jdbc.jar
</p>
</attribute>
<attribute name="username" required="true">
<p>(String) The connection username to be passed to our JDBC driver to establish a connection.
Note that method <code>DataSource.getConnection(username,password)</code>
by default will not use credentials passed into the method,
but will use the ones configured here. See <code>alternateUsernameAllowed</code>
property for more details.
</p>
</attribute>
<attribute name="password" required="true">
<p>(String) The connection password to be passed to our JDBC driver to establish a connection.
Note that method <code>DataSource.getConnection(username,password)</code>
by default will not use credentials passed into the method,
but will use the ones configured here. See <code>alternateUsernameAllowed</code>
property for more details.
</p>
</attribute>
<attribute name="maxActive" required="false">
<p>(int) The maximum number of active connections that can be allocated from this pool at the same time.
The default value is <code>100</code></p>
</attribute>
<attribute name="maxIdle" required="false">
<p>(int) The maximum number of connections that should be kept in the pool at all times.
Default value is <code>maxActive</code>:<code>100</code>
Idle connections are checked periodically (if enabled) and
connections that been idle for longer than <code>minEvictableIdleTimeMillis</code>
will be released. (also see <code>testWhileIdle</code>)</p>
</attribute>
<attribute name="minIdle" required="false">
<p>
(int) The minimum number of established connections that should be kept in the pool at all times.
The connection pool can shrink below this number if validation queries fail.
Default value is derived from <code>initialSize</code>:<code>10</code> (also see <code>testWhileIdle</code>)
</p>
</attribute>
<attribute name="initialSize" required="false">
<p>(int)The initial number of connections that are created when the pool is started.
Default value is <code>10</code></p>
</attribute>
<attribute name="maxWait" required="false">
<p>(int) The maximum number of milliseconds that the pool will wait (when there are no available connections)
for a connection to be returned before throwing an exception.
Default value is <code>30000</code> (30 seconds)</p>
</attribute>
<attribute name="testOnBorrow" required="false">
<p>(boolean) The indication of whether objects will be validated before being borrowed from the pool.
If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another.
In order to have a more efficient validation, see <code>validationInterval</code>.
Default value is <code>false</code>
</p>
</attribute>
<attribute name="testOnConnect" required="false">
<p>(boolean) The indication of whether objects will be validated when a connection is first created.
If an object fails to validate, it will be throw <code>SQLException</code>.
Default value is <code>false</code>
</p>
</attribute>
<attribute name="testOnReturn" required="false">
<p>(boolean) The indication of whether objects will be validated before being returned to the pool.
The default value is <code>false</code>.
</p>
</attribute>
<attribute name="testWhileIdle" required="false">
<p>(boolean) The indication of whether objects will be validated by the idle object evictor (if any).
If an object fails to validate, it will be dropped from the pool.
The default value is <code>false</code> and this property has to be set in order for the
pool cleaner/test thread is to run (also see <code>timeBetweenEvictionRunsMillis</code>)
</p>
</attribute>
<attribute name="validationQuery" required="false">
<p>(String) The SQL query that will be used to validate connections from this pool before returning them to the caller.
If specified, this query does not have to return any data, it just can't throw a <code>SQLException</code>.
The default value is <code>null</code>.
If not specified, connections will be validation by the isValid() method.
Example values are <code>SELECT 1</code>(mysql), <code>select 1 from dual</code>(oracle), <code>SELECT 1</code>(MS Sql Server)
</p>
</attribute>
<attribute name="validationQueryTimeout" required="false">
<p>(int) The timeout in seconds before a connection validation queries fail. This works by calling
<code>java.sql.Statement.setQueryTimeout(seconds)</code> on the statement that executes the <code>validationQuery</code>.
The pool itself doesn't timeout the query, it is still up to the JDBC driver to enforce query timeouts.
A value less than or equal to zero will disable this feature.
The default value is <code>-1</code>.
</p>
</attribute>
<attribute name="validatorClassName" required="false">
<p>(String) The name of a class which implements the
<code>org.apache.tomcat.jdbc.pool.Validator</code> interface and
provides a no-arg constructor (may be implicit). If specified, the
class will be used to create a Validator instance which is then used
instead of any validation query to validate connections. The default
value is <code>null</code>. An example value is
<code>com.mycompany.project.SimpleValidator</code>.
</p>
</attribute>
<attribute name="timeBetweenEvictionRunsMillis" required="false">
<p>(int) The number of milliseconds to sleep between runs of the idle connection validation/cleaner thread.
This value should not be set under 1 second. It dictates how often we check for idle, abandoned connections, and how often
we validate idle connections.
The default value is <code>5000</code> (5 seconds). <br/>
</p>
</attribute>
<attribute name="numTestsPerEvictionRun" required="false">
<p>(int) Property not used in tomcat-jdbc-pool.</p>
</attribute>
<attribute name="minEvictableIdleTimeMillis" required="false">
<p>(int) The minimum amount of time an object may sit idle in the pool before it is eligible for eviction.
The default value is <code>60000</code> (60 seconds).</p>
</attribute>
<attribute name="accessToUnderlyingConnectionAllowed" required="false">
<p>(boolean) Property not used. Access can be achieved by calling <code>unwrap</code> on the pooled connection.
see <code>javax.sql.DataSource</code> interface, or call <code>getConnection</code> through reflection or
cast the object as <code>javax.sql.PooledConnection</code></p>
</attribute>
<attribute name="removeAbandoned" required="false">
<p>(boolean) Flag to remove abandoned connections if they exceed the <code>removeAbandonedTimeout</code>.
If set to true a connection is considered abandoned and eligible for removal if it has been in use
longer than the <code>removeAbandonedTimeout</code> Setting this to <code>true</code> can recover db connections from
applications that fail to close a connection. See also <code>logAbandoned</code>
The default value is <code>false</code>.</p>
</attribute>
<attribute name="removeAbandonedTimeout" required="false">
<p>(int) Timeout in seconds before an abandoned(in use) connection can be removed.
The default value is <code>60</code> (60 seconds). The value should be set to the longest running query your applications
might have.</p>
</attribute>
<attribute name="logAbandoned" required="false">
<p>(boolean) Flag to log stack traces for application code which abandoned a Connection.
Logging of abandoned Connections adds overhead for every Connection borrow because a stack trace has to be generated.
The default value is <code>false</code>.</p>
</attribute>
<attribute name="connectionProperties" required="false">
<p>(String) The connection properties that will be sent to our JDBC driver when establishing new connections.
Format of the string must be [propertyName=property;]*
NOTE - The "user" and "password" properties will be passed explicitly, so they do not need to be included here.
The default value is <code>null</code>.</p>
</attribute>
<attribute name="poolPreparedStatements" required="false">
<p>(boolean) Property not used.</p>
</attribute>
<attribute name="maxOpenPreparedStatements" required="false">
<p>(int) Property not used.</p>
</attribute>
</attributes>
</subsection>
<subsection name="Tomcat JDBC Enhanced Attributes">
<attributes>
<attribute name="initSQL" required="false">
<p>(String) A custom query to be run when a connection is first created.
The default value is <code>null</code>.</p>
</attribute>
<attribute name="jdbcInterceptors" required="false">
<p>(String) A semicolon separated list of classnames extending
<code>org.apache.tomcat.jdbc.pool.JdbcInterceptor</code> class.
See <a href="#Configuring_JDBC_interceptors">Configuring JDBC interceptors</a>
below for more detailed description of syntax and examples.
</p>
<p>
These interceptors will be inserted as an interceptor into the chain
of operations on a <code>java.sql.Connection</code> object.
The default value is <code>null</code>.
</p>
<p>
Predefined interceptors:<br/>
<code>org.apache.tomcat.jdbc.pool.interceptor.<br />ConnectionState</code>
- keeps track of auto commit, read only, catalog and transaction isolation level.<br/>
<code>org.apache.tomcat.jdbc.pool.interceptor.<br />StatementFinalizer</code>
- keeps track of opened statements, and closes them when the connection is returned to the pool.
</p>
<p>
More predefined interceptors are described in detail in the
<a href="#JDBC_interceptors">JDBC Interceptors section</a>.
</p>
</attribute>
<attribute name="validationInterval" required="false">
<p>(long) avoid excess validation, only run validation at most at this frequency - time in milliseconds.
If a connection is due for validation, but has been validated previously within this interval, it will not be validated again.
The default value is <code>3000</code> (3 seconds).</p>
</attribute>
<attribute name="jmxEnabled" required="false">
<p>(boolean) Register the pool with JMX or not.
The default value is <code>true</code>.</p>
</attribute>
<attribute name="fairQueue" required="false">
<p>(boolean) Set to true if you wish that calls to getConnection should be treated
fairly in a true FIFO fashion. This uses the <code>org.apache.tomcat.jdbc.pool.FairBlockingQueue</code>
implementation for the list of the idle connections. The default value is <code>true</code>.
This flag is required when you want to use asynchronous connection retrieval.<br/>
Setting this flag ensures that threads receive connections in the order they arrive.<br/>
During performance tests, there is a very large difference in how locks
and lock waiting is implemented. When <code>fairQueue=true</code>
there is a decision making process based on what operating system the system is running.
If the system is running on Linux (property <code>os.name=Linux</code>.
To disable this Linux specific behavior and still use the fair queue, simply add the property
<code>org.apache.tomcat.jdbc.pool.FairBlockingQueue.ignoreOS=true</code> to your system properties
before the connection pool classes are loaded.
</p>
</attribute>
<attribute name="abandonWhenPercentageFull" required="false">
<p>(int) Connections that have been abandoned (timed out) wont get closed and reported up unless
the number of connections in use are above the percentage defined by <code>abandonWhenPercentageFull</code>.
The value should be between 0-100.
The default value is <code>0</code>, which implies that connections are eligible for closure as soon
as <code>removeAbandonedTimeout</code> has been reached.</p>
</attribute>
<attribute name="maxAge" required="false">
<p>(long) Time in milliseconds to keep this connection. This attribute
works both when returning connection and when borrowing connection.
When a connection is borrowed from the pool, the pool will check to see
if the <code>now - time-when-connected > maxAge</code> has been reached
, and if so, it reconnects before borrow it. When a connection is
returned to the pool, the pool will check to see if the
<code>now - time-when-connected > maxAge</code> has been reached, and
if so, it closes the connection rather than returning it to the pool.
The default value is <code>0</code>, which implies that connections
will be left open and no age check will be done upon borrowing from the
pool and returning the connection to the pool.</p>
</attribute>
<attribute name="useEquals" required="false">
<p>(boolean) Set to true if you wish the <code>ProxyConnection</code> class to use <code>String.equals</code> and set to <code>false</code>
when you wish to use <code>==</code> when comparing method names. This property does not apply to added interceptors as those are configured individually.
The default value is <code>true</code>.
</p>
</attribute>
<attribute name="suspectTimeout" required="false">
<p>(int) Timeout value in seconds. Default value is <code>0</code>.<br/>
Similar to to the <code>removeAbandonedTimeout</code> value but instead of treating the connection
as abandoned, and potentially closing the connection, this simply logs the warning if
<code>logAbandoned</code> is set to true. If this value is equal or less than 0, no suspect
checking will be performed. Suspect checking only takes place if the timeout value is larger than 0 and
the connection was not abandoned or if abandon check is disabled. If a connection is suspect a WARN message gets
logged and a JMX notification gets sent once.
</p>
</attribute>
<attribute name="rollbackOnReturn" required="false">
<p>(boolean) If <code>autoCommit==false</code> then the pool can terminate the transaction by calling rollback on the connection as it is returned to the pool
Default value is <code>false</code>.<br/>
</p>
</attribute>
<attribute name="commitOnReturn" required="false">
<p>(boolean) If <code>autoCommit==false</code> then the pool can complete the transaction by calling commit on the connection as it is returned to the pool
If <code>rollbackOnReturn==true</code> then this attribute is ignored.
Default value is <code>false</code>.<br/>
</p>
</attribute>
<attribute name="alternateUsernameAllowed" required="false">
<p>(boolean) By default, the jdbc-pool will ignore the
<a href="http://docs.oracle.com/javase/6/docs/api/javax/sql/DataSource.html#getConnection(java.lang.String,%20java.lang.String)"><code>DataSource.getConnection(username,password)</code></a>
call, and simply return a previously pooled connection under the globally configured properties <code>username</code> and <code>password</code>, for performance reasons.
</p>
<p>
The pool can however be configured to allow use of different credentials
each time a connection is requested. To enable the functionality described in the
<a href="http://docs.oracle.com/javase/6/docs/api/javax/sql/DataSource.html#getConnection(java.lang.String,%20java.lang.String)"><code>DataSource.getConnection(username,password)</code></a>
call, simply set the property <code>alternateUsernameAllowed</code>
to <code>true</code>.<br />
Should you request a connection with the credentials user1/password1 and the connection
was previously connected using different user2/password2, the connection will be closed,
and reopened with the requested credentials. This way, the pool size is still managed
on a global level, and not on a per schema level. <br/>
The default value is <code>false</code>.<br/>
This property was added as an enhancement to <a href="https://bz.apache.org/bugzilla/show_bug.cgi?id=50025">bug 50025</a>.
</p>
</attribute>
<attribute name="dataSource" required="false">
<p>(javax.sql.DataSource) Inject a data source to the connection pool, and the pool will use the data source to retrieve connections instead of establishing them using the <code>java.sql.Driver</code> interface.
This is useful when you wish to pool XA connections or connections established using a data source instead of a connection string. Default value is <code>null</code>
</p>
</attribute>
<attribute name="dataSourceJNDI" required="false">
<p>(String) The JNDI name for a data source to be looked up in JNDI and then used to establish connections to the database. See the <code>dataSource</code> attribute. Default value is <code>null</code>
</p>
</attribute>
<attribute name="useDisposableConnectionFacade" required="false">
<p>(boolean) Set this to true if you wish to put a facade on your connection so that it cannot be reused after it has been closed. This prevents a thread holding on to a
reference of a connection it has already called closed on, to execute queries on it. Default value is <code>true</code>.
</p>
</attribute>
<attribute name="logValidationErrors" required="false">
<p>(boolean) Set this to true to log errors during the validation phase to the log file. If set to true, errors will be logged as SEVERE. Default value is <code>false</code> for backwards compatibility.
</p>
</attribute>
<attribute name="propagateInterruptState" required="false">
<p>(boolean) Set this to true to propagate the interrupt state for a thread that has been interrupted (not clearing the interrupt state). Default value is <code>false</code> for backwards compatibility.
</p>
</attribute>
<attribute name="ignoreExceptionOnPreLoad" required="false">
<p>(boolean) Flag whether ignore error of connection creation while initializing the pool.
Set to true if you want to ignore error of connection creation while initializing the pool.
Set to false if you want to fail the initialization of the pool by throwing exception.
The default value is <code>false</code>.
</p>
</attribute>
<attribute name="useStatementFacade" required="false">
<p>(boolean) Set this to true if you wish to wrap statements in order to
enable <code>equals()</code> and <code>hashCode()</code> methods to be
called on the closed statements if any statement proxy is set.
Default value is <code>true</code>.
</p>
</attribute>
</attributes>
</subsection>
</section>
<section name="Advanced usage">
<subsection name="JDBC interceptors">
<p>To see an example of how to use an interceptor, take a look at
<code>org.apache.tomcat.jdbc.pool.interceptor.ConnectionState</code>.
This simple interceptor is a cache of three attributes, transaction isolation level, auto commit and read only state,
in order for the system to avoid not needed roundtrips to the database.
</p>
<p>Further interceptors will be added to the core of the pool as the need arises. Contributions are always welcome!</p>
<p>Interceptors are of course not limited to just <code>java.sql.Connection</code> but can be used to wrap any
of the results from a method invocation as well. You could build query performance analyzer that provides JMX notifications when a
query is running longer than the expected time.</p>
</subsection>
<subsection name="Configuring JDBC interceptors">
<p>Configuring JDBC interceptors is done using the <b>jdbcInterceptors</b> property.
The property contains a list of semicolon separated class names. If the
classname is not fully qualified it will be prefixed with the
<code>org.apache.tomcat.jdbc.pool.interceptor.</code> prefix.
</p>
<p>Example:<br/>
<code>
jdbcInterceptors=&quot;org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer&quot;
</code>
<br/>
is the same as
<br/>
<code> jdbcInterceptors=&quot;ConnectionState;StatementFinalizer&quot;</code>
</p>
<p>
Interceptors can have properties as well. Properties for an interceptor
are specified within parentheses after the class name. Several properties
are separated by commas.
</p>
<p>Example:<br/>
<code>
jdbcInterceptors=&quot;ConnectionState;StatementFinalizer(useEquals=true)&quot;
</code>
</p>
<p>
Extra whitespace characters around class names, property names and values
are ignored.
</p>
</subsection>
<subsection name="org.apache.tomcat.jdbc.pool.JdbcInterceptor">
<p>Abstract base class for all interceptors, cannot be instantiated.</p>
<attributes>
<attribute name="useEquals" required="false">
<p>(boolean) Set to true if you wish the <code>ProxyConnection</code> class to use <code>String.equals</code> and set to <code>false</code>
when you wish to use <code>==</code> when comparing method names.
The default value is <code>true</code>.
</p>
</attribute>
</attributes>
</subsection>
<subsection name="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState">
<p>Caches the connection for the following attributes <code>autoCommit</code>, <code>readOnly</code>,
<code>transactionIsolation</code> and <code>catalog</code>.
It is a performance enhancement to avoid roundtrip to the database when getters are called or setters are called with an already set value.
</p>
<attributes>
</attributes>
</subsection>
<subsection name="org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer">
<p>Keeps track of all statements created using <code>createStatement</code>, <code>prepareStatement</code> or <code>prepareCall</code>
and closes these statements when the connection is returned to the pool.
</p>
<attributes>
<attribute name="trace" required="false">
<p>(boolean as String) Enable tracing of unclosed statements.
When enabled and a connection is closed, and statements are not closed,
the interceptor will log all stack traces.
The default value is <code>false</code>.
</p>
</attribute>
</attributes>
</subsection>
<subsection name="org.apache.tomcat.jdbc.pool.interceptor.StatementCache">
<p>Caches <code>PreparedStatement</code> and/or <code>CallableStatement</code>
instances on a connection.
</p>
<p>The statements are cached per connection.
The count limit is counted globally for all connections that belong to
the same pool. Once the count reaches <code>max</code>, subsequent
statements are not returned to the cache and are closed immediately.
</p>
<attributes>
<attribute name="prepared" required="false">
<p>(boolean as String) Enable caching of <code>PreparedStatement</code>
instances created using <code>prepareStatement</code> calls.
The default value is <code>true</code>.
</p>
</attribute>
<attribute name="callable" required="false">
<p>(boolean as String) Enable caching of <code>CallableStatement</code>
instances created using <code>prepareCall</code> calls.
The default value is <code>false</code>.
</p>
</attribute>
<attribute name="max" required="false">
<p>(int as String) Limit on the count of cached statements across
the connection pool.
The default value is <code>50</code>.
</p>
</attribute>
</attributes>
</subsection>
<subsection name="org.apache.tomcat.jdbc.pool.interceptor.StatementDecoratorInterceptor">
<p>See <bug>48392</bug>. Interceptor to wrap statements and result sets in order to prevent access to the actual connection
using the methods <code>ResultSet.getStatement().getConnection()</code> and <code>Statement.getConnection()</code>
</p>
<attributes>
</attributes>
</subsection>
<subsection name="org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor">
<p>Automatically calls <code>java.sql.Statement.setQueryTimeout(seconds)</code> when a new statement is created.
The pool itself doesn't timeout the query, it is still up to the JDBC driver to enforce query timeouts.
</p>
<attributes>
<attribute name="queryTimeout" required="true">
<p>(int as String) The number of seconds to set for the query timeout.
A value less than or equal to zero will disable this feature.
The default value is <code>1</code> seconds.
</p>
</attribute>
</attributes>
</subsection>
<subsection name="org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport">
<p>Keeps track of query performance and issues log entries when queries exceed a time threshold of fail.
The log level used is <code>WARN</code>
</p>
<attributes>
<attribute name="threshold" required="false">
<p>(int as String) The number of milliseconds a query has to exceed before issuing a log alert.
The default value is <code>1000</code> milliseconds.
</p>
</attribute>
<attribute name="maxQueries" required="false">
<p>(int as String) The maximum number of queries to keep track of in order to preserve memory space.
A value less than or equal to 0 will disable this feature.
The default value is <code>1000</code>.
</p>
</attribute>
<attribute name="logSlow" required="false">
<p>(boolean as String) Set to <code>true</code> if you wish to log slow queries.
The default value is <code>true</code>.
</p>
</attribute>
<attribute name="logFailed" required="false">
<p>(boolean as String) Set to <code>true</code> if you wish to log failed queries.
The default value is <code>false</code>.
</p>
</attribute>
</attributes>
</subsection>
<subsection name="org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx">
<p>Extends the <code>SlowQueryReport</code> and in addition to log entries it issues JMX notification
for monitoring tools to react to. Inherits all the attributes from its parent class.
This class uses Tomcat's JMX engine so it wont work outside of the Tomcat container.
By default, JMX notifications are sent through the ConnectionPool mbean if it is enabled.
The <code>SlowQueryReportJmx</code> can also register an MBean if <code>notifyPool=false</code>
</p>
<attributes>
<attribute name="notifyPool" required="false">
<p>(boolean as String) Set to false if you want JMX notifications to go to the <code>SlowQueryReportJmx</code> MBean
The default value is <code>true</code>.
</p>
</attribute>
<attribute name="objectName" required="false">
<p>(String) Define a valid <code>javax.management.ObjectName</code> string that will be used to register this object with the platform mbean server
The default value is <code>null</code> and the object will be registered using
tomcat.jdbc:type=org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx,name=the-name-of-the-pool
</p>
</attribute>
</attributes>
</subsection>
<subsection name="org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer">
<p>
The abandoned timer starts when a connection is checked out from the pool.
This means if you have a 30second timeout and run 10x10second queries using the connection
it will be marked abandoned and potentially reclaimed depending on the <code>abandonWhenPercentageFull</code>
attribute.
Using this interceptor it will reset the checkout timer every time you perform an operation on the connection or execute a
query successfully.
</p>
<attributes>
</attributes>
</subsection>
</section>
<section name="Code Example">
<p>Other examples of Tomcat configuration for JDBC usage can be found <a href="https://tomcat.apache.org/tomcat-8.5-doc/jndi-datasource-examples-howto.html">in the Tomcat documentation</a>. </p>
<subsection name="Plain Ol' Java">
<p>Here is a simple example of how to create and use a data source.</p>
<source><![CDATA[ import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class SimplePOJOExample {
public static void main(String[] args) throws Exception {
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:mysql://localhost:3306/mysql");
p.setDriverClassName("com.mysql.jdbc.Driver");
p.setUsername("root");
p.setPassword("password");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors(
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
"org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
DataSource datasource = new DataSource();
datasource.setPoolProperties(p);
Connection con = null;
try {
con = datasource.getConnection();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from user");
int cnt = 1;
while (rs.next()) {
System.out.println((cnt++)+". Host:" +rs.getString("Host")+
" User:"+rs.getString("User")+" Password:"+rs.getString("Password"));
}
rs.close();
st.close();
} finally {
if (con!=null) try {con.close();}catch (Exception ignore) {}
}
}
}]]></source>
</subsection>
<subsection name="As a Resource">
<p>And here is an example on how to configure a resource for JNDI lookups</p>
<source><![CDATA[<Resource name="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
testWhileIdle="true"
testOnBorrow="true"
testOnReturn="false"
validationQuery="SELECT 1"
validationInterval="30000"
timeBetweenEvictionRunsMillis="30000"
maxActive="100"
minIdle="10"
maxWait="10000"
initialSize="10"
removeAbandonedTimeout="60"
removeAbandoned="true"
logAbandoned="true"
minEvictableIdleTimeMillis="30000"
jmxEnabled="true"
jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
username="root"
password="password"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mysql"/>]]></source>
</subsection>
<subsection name="Asynchronous Connection Retrieval">
<p> The Tomcat JDBC connection pool supports asynchronous connection retrieval without adding additional threads to the
pool library. It does this by adding a method to the data source called <code>Future&lt;Connection&gt; getConnectionAsync()</code>.
In order to use the async retrieval, two conditions must be met:
</p>
<ol>
<li>You must configure the <code>fairQueue</code> property to be <code>true</code>.</li>
<li>You will have to cast the data source to <code>org.apache.tomcat.jdbc.pool.DataSource</code></li>
</ol>
An example of using the async feature is show below.
<source><![CDATA[ Connection con = null;
try {
Future<Connection> future = datasource.getConnectionAsync();
while (!future.isDone()) {
System.out.println("Connection is not yet available. Do some background work");
try {
Thread.sleep(100); //simulate work
}catch (InterruptedException x) {
Thread.currentThread().interrupt();
}
}
con = future.get(); //should return instantly
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from user");]]></source>
</subsection>
<subsection name="Interceptors">
<p>Interceptors are a powerful way to enable, disable or modify functionality on a specific connection or its sub components.
There are many different use cases for when interceptors are useful. By default, and for performance reasons, the connection pool is stateless.
The only state the pool itself inserts are <code>defaultAutoCommit</code>, <code>defaultReadOnly</code>, <code>defaultTransactionIsolation</code>, <code>defaultCatalog</code> if
these are set. These 4 properties are only set upon connection creation. Should these properties be modified during the usage of the connection,
the pool itself will not reset them.</p>
<p>An interceptor has to extend the <code>org.apache.tomcat.jdbc.pool.JdbcInterceptor</code> class. This class is fairly simple,
You will need to have a no arg constructor</p>
<source><![CDATA[ public JdbcInterceptor() {
}]]></source>
<p>
When a connection is borrowed from the pool, the interceptor can initialize or in some other way react to the event by implementing the
</p>
<source><![CDATA[ public abstract void reset(ConnectionPool parent, PooledConnection con);]]></source>
<p>
method. This method gets called with two parameters, a reference to the connection pool itself <code>ConnectionPool parent</code>
and a reference to the underlying connection <code>PooledConnection con</code>.
</p>
<p>
When a method on the <code>java.sql.Connection</code> object is invoked, it will cause the
</p>
<source><![CDATA[ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable]]></source>
<p>
method to get invoked. The <code>Method method</code> is the actual method invoked, and <code>Object[] args</code> are the arguments.
To look at a very simple example, where we demonstrate how to make the invocation to <code>java.sql.Connection.close()</code> a noop
if the connection has been closed
</p>
<source><![CDATA[ if (CLOSE_VAL==method.getName()) {
if (isClosed()) return null; //noop for already closed.
}
return super.invoke(proxy,method,args);]]></source>
<p>
There is an observation being made. It is the comparison of the method name. One way to do this would be to do
<code>&quot;close&quot;.equals(method.getName())</code>.
Above we see a direct reference comparison between the method name and <code>static final String</code> reference.
According to the JVM spec, method names and static final String end up in a shared constant pool, so the reference comparison should work.
One could of course do this as well:
</p>
<source><![CDATA[ if (compare(CLOSE_VAL,method)) {
if (isClosed()) return null; //noop for already closed.
}
return super.invoke(proxy,method,args);]]></source>
<p>
The <code>compare(String,Method)</code> will use the <code>useEquals</code> flag on an interceptor and do either reference comparison or
a string value comparison when the <code>useEquals=true</code> flag is set.
</p>
<p>Pool start/stop<br/>
When the connection pool is started or closed, you can be notifed. You will only be notified once per interceptor class
even though it is an instance method. and you will be notified using an interceptor currently not attached to a pool.
</p>
<source><![CDATA[ public void poolStarted(ConnectionPool pool) {
}
public void poolClosed(ConnectionPool pool) {
}]]></source>
<p>
When overriding these methods, don't forget to call super if you are extending a class other than <code>JdbcInterceptor</code>
</p>
<p>Configuring interceptors<br/>
Interceptors are configured using the <code>jdbcInterceptors</code> property or the <code>setJdbcInterceptors</code> method.
An interceptor can have properties, and would be configured like this
</p>
<source><![CDATA[ String jdbcInterceptors=
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState(useEquals=true,fast=yes)"]]></source>
<p>Interceptor properties<br/>
Since interceptors can have properties, you need to be able to read the values of these properties within your
interceptor. Taking an example like the one above, you can override the <code>setProperties</code> method.
</p>
<source><![CDATA[ public void setProperties(Map<String, InterceptorProperty> properties) {
super.setProperties(properties);
final String myprop = "myprop";
InterceptorProperty p1 = properties.get(myprop);
if (p1!=null) {
setMyprop(Long.parseLong(p1.getValue()));
}
}]]></source>
</subsection>
<subsection name="Getting the actual JDBC connection">
<p>Connection pools create wrappers around the actual connection in order to properly pool them.
We also create interceptors in these wrappers to be able to perform certain functions.
If there is a need to retrieve the actual connection, one can do so using the <code>javax.sql.PooledConnection</code>
interface.
</p>
<source><![CDATA[ Connection con = datasource.getConnection();
Connection actual = ((javax.sql.PooledConnection)con).getConnection();]]></source>
</subsection>
</section>
<section name="Building">
<p>We build the JDBC pool code with 1.6, but it is backwards compatible down to 1.5 for runtime environment. For unit test, we use 1.6 and higher</p>
<p>Other examples of Tomcat configuration for JDBC usage can be found <a href="https://tomcat.apache.org/tomcat-8.5-doc/jndi-datasource-examples-howto.html">in the Tomcat documentation</a>. </p>
<subsection name="Building from source">
<p>Building is pretty simple. The pool has a dependency on <code>tomcat-juli.jar</code> and in case you want the <code>SlowQueryReportJmx</code></p>
<source><![CDATA[ javac -classpath tomcat-juli.jar \
-d . \
org/apache/tomcat/jdbc/pool/*.java \
org/apache/tomcat/jdbc/pool/interceptor/*.java \
org/apache/tomcat/jdbc/pool/jmx/*.java]]></source>
<p>
A build file can be found in the Tomcat <a href="https://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/">source repository</a>.
</p>
<p>
As a convenience, a build file is also included where a simple build command will generate all files needed.
</p>
<source> ant download (downloads dependencies)
ant build (compiles and generates .jar files)
ant dist (creates a release package)
ant test (runs tests, expects a test database to be setup)</source>
<p>
The system is structured for a Maven build, but does generate release artifacts. Just the library itself.
</p>
</subsection>
</section>
</body>
</document>

View File

@@ -0,0 +1,249 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
Stylesheet that generates "package.html" for Javadoc tool
from jdbc-pool.xml documentation file.
It is based on "tomcat-docs" stylesheet, but it needs to avoid
generating complicated headers and footers, as those cannot be
digested by Javadoc tool and break layout of javadoc pages.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<!-- Output method -->
<xsl:output method="html"
encoding="UTF-8"
indent="no"/>
<!-- Defined parameters (overrideable) -->
<xsl:param name="relative-path" select="'.'"/>
<xsl:param name="void-image" select="'/images/void.gif'"/>
<xsl:param name="standalone" select="''"/>
<xsl:param name="buglink" select="'https://bz.apache.org/bugzilla/show_bug.cgi?id='"/>
<xsl:param name="revlink" select="'https://svn.apache.org/viewvc?view=rev&amp;rev='"/>
<!-- Defined variables (non-overrideable) -->
<xsl:variable name="body-bg" select="'#ffffff'"/>
<xsl:variable name="body-fg" select="'#000000'"/>
<xsl:variable name="body-link" select="'#525D76'"/>
<xsl:variable name="banner-bg" select="'#525D76'"/>
<xsl:variable name="banner-fg" select="'#ffffff'"/>
<xsl:variable name="sub-banner-bg" select="'#828DA6'"/>
<xsl:variable name="sub-banner-fg" select="'#ffffff'"/>
<xsl:variable name="source-color" select="'#023264'"/>
<xsl:variable name="attributes-color" select="'#023264'"/>
<xsl:variable name="table-th-bg" select="'#039acc'"/>
<xsl:variable name="table-td-bg" select="'#a0ddf0'"/>
<!-- Process an entire document into an HTML page -->
<xsl:template match="document">
<xsl:variable name="project"
select="document('project.xml')/project"/>
<html>
<head>
<title><xsl:value-of select="project/title"/> - <xsl:value-of select="properties/title"/></title>
</head>
<body bgcolor="{$body-bg}" text="{$body-fg}" link="{$body-link}"
alink="{$body-link}" vlink="{$body-link}">
<h2><xsl:value-of select="properties/title"/>.</h2>
<xsl:apply-templates select="body/section"/>
</body>
</html>
</xsl:template>
<!-- Process a documentation section -->
<xsl:template match="section">
<xsl:variable name="name">
<xsl:value-of select="@name"/>
</xsl:variable>
<table border="0" cellspacing="0" cellpadding="2">
<!-- Section heading -->
<tr><td bgcolor="{$banner-bg}">
<font color="{$banner-fg}" face="arial,helvetica.sanserif">
<a name="{$name}">
<strong><xsl:value-of select="@name"/></strong></a></font>
</td></tr>
<!-- Section body -->
<tr><td><blockquote>
<xsl:apply-templates/>
</blockquote></td></tr>
</table>
</xsl:template>
<!-- Process a documentation subsection -->
<xsl:template match="subsection">
<xsl:variable name="name">
<xsl:value-of select="@name"/>
</xsl:variable>
<table border="0" cellspacing="0" cellpadding="2">
<!-- Subsection heading -->
<tr><td bgcolor="{$sub-banner-bg}">
<font color="{$sub-banner-fg}" face="arial,helvetica.sanserif">
<a name="{$name}">
<strong><xsl:value-of select="@name"/></strong></a></font>
</td></tr>
<!-- Subsection body -->
<tr><td><blockquote>
<xsl:apply-templates/>
</blockquote></td></tr>
</table>
</xsl:template>
<!-- Process a source code example -->
<xsl:template match="source">
<xsl:variable name="void">
<xsl:value-of select="$relative-path"/><xsl:value-of select="$void-image"/>
</xsl:variable>
<div align="left">
<table cellspacing="4" cellpadding="0" border="0">
<tr>
<td bgcolor="{$source-color}" width="1" height="1">
<img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
</td>
<td bgcolor="{$source-color}" height="1">
<img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
</td>
<td bgcolor="{$source-color}" width="1" height="1">
<img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
</td>
</tr>
<tr>
<td bgcolor="{$source-color}" width="1">
<img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
</td>
<td bgcolor="#ffffff" height="1"><pre>
<xsl:value-of select="."/>
</pre></td>
<td bgcolor="{$source-color}" width="1">
<img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
</td>
</tr>
<tr>
<td bgcolor="{$source-color}" width="1" height="1">
<img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
</td>
<td bgcolor="{$source-color}" height="1">
<img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
</td>
<td bgcolor="{$source-color}" width="1" height="1">
<img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
</td>
</tr>
</table>
</div>
</xsl:template>
<!-- Process an attributes list with nested attribute elements -->
<xsl:template match="attributes">
<table border="1" cellpadding="5">
<tr>
<th width="15%" bgcolor="{$attributes-color}">
<font color="#ffffff">Attribute</font>
</th>
<th width="85%" bgcolor="{$attributes-color}">
<font color="#ffffff">Description</font>
</th>
</tr>
<xsl:for-each select="attribute">
<tr>
<td align="left" valign="center">
<xsl:if test="@required = 'true'">
<strong><code><xsl:value-of select="@name"/></code></strong>
</xsl:if>
<xsl:if test="@required != 'true'">
<code><xsl:value-of select="@name"/></code>
</xsl:if>
</td>
<td align="left" valign="center">
<xsl:apply-templates/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<!-- Process a properties list with nested property elements -->
<xsl:template match="properties">
<table border="1" cellpadding="5">
<tr>
<th width="15%" bgcolor="{$attributes-color}">
<font color="#ffffff">Property</font>
</th>
<th width="85%" bgcolor="{$attributes-color}">
<font color="#ffffff">Description</font>
</th>
</tr>
<xsl:for-each select="property">
<tr>
<td align="left" valign="center">
<code><xsl:value-of select="@name"/></code>
</td>
<td align="left" valign="center">
<xsl:apply-templates/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<!-- Fix relative links in printer friendly versions of the docs -->
<xsl:template match="a">
<xsl:variable name="href" select="@href"/>
<xsl:choose>
<xsl:when test="$standalone = 'standalone'">
<xsl:apply-templates/>
</xsl:when>
<xsl:when test="$href != ''">
<a href="{$href}"><xsl:apply-templates/></a>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="name" select="@name"/>
<a name="{$name}"><xsl:apply-templates/></a>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Link to a bug report -->
<xsl:template match="bug">
<xsl:variable name="link"><xsl:value-of select="$buglink"/><xsl:value-of select="text()"/></xsl:variable>
<a href="{$link}"><xsl:apply-templates/></a>
</xsl:template>
<!-- Link to a SVN revision report -->
<xsl:template match="rev">
<xsl:variable name="link"><xsl:value-of select="$revlink"/><xsl:value-of select="text()"/></xsl:variable>
<a href="{$link}"><xsl:apply-templates/></a>
</xsl:template>
<!-- Process everything else by just passing it through -->
<xsl:template match="*|@*">
<xsl:copy>
<xsl:apply-templates select="@*|*|text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project name="Apache Tomcat JDBC Pool Documentation"
href="http://tomcat.apache.org/">
<title>Apache Tomcat JDBC Pool</title>
<logo href="/images/tomcat.gif">
The Apache Tomcat Servlet/JSP Container
</logo>
<body>
</body>
</project>

158
modules/jdbc-pool/pom.xml Normal file
View File

@@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache</groupId>
<artifactId>apache</artifactId>
<version>15</version>
</parent>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>8.0.15-SNAPSHOT</version>
<packaging>jar</packaging>
<name>jdbc-pool</name>
<url>https://people.apache.org/~fhanik/jdbc-pool/</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<mailingLists>
<mailingList>
<name>Development List</name>
<subscribe>dev-subscribe@tomcat.apache.org</subscribe>
<unsubscribe>dev-unsubscribe@tomcat.apache.org</unsubscribe>
<post>dev@tomcat.apache.org</post>
</mailingList>
<mailingList>
<name>Users List</name>
<subscribe>users-subscribe@tomcat.apache.org</subscribe>
<unsubscribe>users-unsubscribe@tomcat.apache.org</unsubscribe>
<post>users@tomcat.apache.org</post>
</mailingList>
</mailingLists>
<scm>
<connection>scm:svn:https://svn.apache.org/repos/asf/tomcat/trunk/modules/jdbc-pool</connection>
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/tomcat/trunk/modules/jdbc-pool</developerConnection>
<url>https://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool</url>
</scm>
<dependencies>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>juli</artifactId>
<version>6.0.32</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>8.0.14</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.152</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<optimize>true</optimize>
<debug>true</debug>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,22 @@
Manifest-Version: 1.0
Export-Package: org.apache.tomcat.jdbc.naming;uses:="javax.naming,org.ap
ache.juli.logging,javax.naming.spi";version="@VERSION@",org.apache.tomc
at.jdbc.pool;uses:="org.apache.juli.logging,javax.sql,org.apache.tomcat
.jdbc.pool.jmx,javax.management,javax.naming,javax.naming.spi,org.apach
e.tomcat.jdbc.pool.interceptor";version="@VERSION@",org.apache.tomcat.j
dbc.pool.interceptor;uses:="org.apache.tomcat.jdbc.pool,org.apache.juli
.logging,javax.management.openmbean,javax.management";version="@VERSION@
",org.apache.tomcat.jdbc.pool.jmx;uses:="org.apache.tomcat.jdbc.pool,or
g.apache.juli.logging,javax.management";version="@VERSION@"
Bundle-Vendor: Apache Software Foundation
Bundle-Version: @VERSION@
Bundle-Name: Apache Tomcat JDBC Connection Pool
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.apache.tomcat.jdbc
Import-Package:
javax.management;version="0",
javax.management.openmbean;version="0",
javax.naming;version="0",
javax.naming.spi;version="0",
javax.sql;version="0",
org.apache.juli.logging;version="0"

View File

@@ -0,0 +1,233 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.naming;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.ClassLoaderUtil;
/**
* Simple way of configuring generic resources by using reflection.
* Example usage:
* <pre><code>
* &lt;Resource factory=&quot;org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory&quot;
* name=&quot;jdbc/test&quot;
* type=&quot;org.apache.derby.jdbc.ClientXADataSource&quot;
* databaseName=&quot;sample&quot;
* createDatabase=&quot;create&quot;
* serverName=&quot;localhost&quot;
* port=&quot;1527&quot;/&gt;
* </code></pre>
*
*/
public class GenericNamingResourcesFactory implements ObjectFactory {
private static final Log log = LogFactory.getLog(GenericNamingResourcesFactory.class);
@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
if ((obj == null) || !(obj instanceof Reference)) {
return null;
}
Reference ref = (Reference) obj;
Enumeration<RefAddr> refs = ref.getAll();
String type = ref.getClassName();
Object o =
ClassLoaderUtil.loadClass(
type,
GenericNamingResourcesFactory.class.getClassLoader(),
Thread.currentThread().getContextClassLoader()).getConstructor().newInstance();
while (refs.hasMoreElements()) {
RefAddr addr = refs.nextElement();
String param = addr.getType();
String value = null;
if (addr.getContent()!=null) {
value = addr.getContent().toString();
}
if (setProperty(o, param, value)) {
} else {
log.debug("Property not configured["+param+"]. No setter found on["+o+"].");
}
}
return o;
}
@SuppressWarnings("null") // setPropertyMethodVoid can't be null when used
private static boolean setProperty(Object o, String name, String value) {
if (log.isDebugEnabled())
log.debug("IntrospectionUtils: setProperty(" +
o.getClass() + " " + name + "=" + value + ")");
String setter = "set" + capitalize(name);
try {
Method methods[] = o.getClass().getMethods();
Method setPropertyMethodVoid = null;
Method setPropertyMethodBool = null;
// First, the ideal case - a setFoo( String ) method
for (int i = 0; i < methods.length; i++) {
Class<?> paramT[] = methods[i].getParameterTypes();
if (setter.equals(methods[i].getName()) && paramT.length == 1
&& "java.lang.String".equals(paramT[0].getName())) {
methods[i].invoke(o, new Object[] { value });
return true;
}
}
// Try a setFoo ( int ) or ( boolean )
for (int i = 0; i < methods.length; i++) {
boolean ok = true;
if (setter.equals(methods[i].getName())
&& methods[i].getParameterTypes().length == 1) {
// match - find the type and invoke it
Class<?> paramType = methods[i].getParameterTypes()[0];
Object params[] = new Object[1];
// Try a setFoo ( int )
if ("java.lang.Integer".equals(paramType.getName())
|| "int".equals(paramType.getName())) {
try {
params[0] = Integer.valueOf(value);
} catch (NumberFormatException ex) {
ok = false;
}
// Try a setFoo ( long )
}else if ("java.lang.Long".equals(paramType.getName())
|| "long".equals(paramType.getName())) {
try {
params[0] = Long.valueOf(value);
} catch (NumberFormatException ex) {
ok = false;
}
// Try a setFoo ( boolean )
} else if ("java.lang.Boolean".equals(paramType.getName())
|| "boolean".equals(paramType.getName())) {
params[0] = Boolean.valueOf(value);
// Try a setFoo ( InetAddress )
} else if ("java.net.InetAddress".equals(paramType
.getName())) {
try {
params[0] = InetAddress.getByName(value);
} catch (UnknownHostException exc) {
if (log.isDebugEnabled())
log.debug("IntrospectionUtils: Unable to resolve host name:" + value);
ok = false;
}
// Unknown type
} else {
if (log.isDebugEnabled())
log.debug("IntrospectionUtils: Unknown type " +
paramType.getName());
}
if (ok) {
methods[i].invoke(o, params);
return true;
}
}
// save "setProperty" for later
if ("setProperty".equals(methods[i].getName())) {
if (methods[i].getReturnType()==Boolean.TYPE){
setPropertyMethodBool = methods[i];
}else {
setPropertyMethodVoid = methods[i];
}
}
}
// Ok, no setXXX found, try a setProperty("name", "value")
if (setPropertyMethodBool != null || setPropertyMethodVoid != null) {
Object params[] = new Object[2];
params[0] = name;
params[1] = value;
if (setPropertyMethodBool != null) {
try {
return ((Boolean) setPropertyMethodBool.invoke(o, params)).booleanValue();
}catch (IllegalArgumentException biae) {
//the boolean method had the wrong
//parameter types. lets try the other
if (setPropertyMethodVoid!=null) {
setPropertyMethodVoid.invoke(o, params);
return true;
}else {
throw biae;
}
}
} else {
setPropertyMethodVoid.invoke(o, params);
return true;
}
}
} catch (IllegalArgumentException ex2) {
log.warn("IAE " + o + " " + name + " " + value, ex2);
} catch (SecurityException ex1) {
if (log.isDebugEnabled())
log.debug("IntrospectionUtils: SecurityException for " +
o.getClass() + " " + name + "=" + value + ")", ex1);
} catch (IllegalAccessException iae) {
if (log.isDebugEnabled())
log.debug("IntrospectionUtils: IllegalAccessException for " +
o.getClass() + " " + name + "=" + value + ")", iae);
} catch (InvocationTargetException ie) {
Throwable cause = ie.getCause();
if (cause instanceof ThreadDeath) {
throw (ThreadDeath) cause;
}
if (cause instanceof VirtualMachineError) {
throw (VirtualMachineError) cause;
}
if (log.isDebugEnabled())
log.debug("IntrospectionUtils: InvocationTargetException for " +
o.getClass() + " " + name + "=" + value + ")", ie);
}
return false;
}
public static String capitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toUpperCase(chars[0]);
return new String(chars);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
public class ClassLoaderUtil {
private static final Log log = LogFactory.getLog(ClassLoaderUtil.class);
private static final boolean onlyAttemptFirstLoader =
Boolean.parseBoolean(System.getProperty("org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader", "false"));
public static Class<?> loadClass(String className, ClassLoader... classLoaders) throws ClassNotFoundException {
ClassNotFoundException last = null;
StringBuilder errorMsg = null;
for (ClassLoader cl : classLoaders) {
try {
if (cl!=null) {
if (log.isDebugEnabled()) {
log.debug("Attempting to load class["+className+"] from "+cl);
}
return Class.forName(className, true, cl);
} else {
throw new ClassNotFoundException("Classloader is null");
}
} catch (ClassNotFoundException x) {
last = x;
if (errorMsg==null) {
errorMsg = new StringBuilder();
} else {
errorMsg.append(';');
}
errorMsg.append("ClassLoader:");
errorMsg.append(cl);
}
if (onlyAttemptFirstLoader) {
break;
}
}
throw new ClassNotFoundException("Unable to load class: "+className+" from "+errorMsg, last);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.util.Hashtable;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.jmx.JmxUtil;
/**
* A DataSource that can be instantiated through IoC and implements the DataSource interface
* since the DataSourceProxy is used as a generic proxy.
* The DataSource simply wraps a {@link ConnectionPool} in order to provide a standard interface to the user.
* @version 1.0
*/
public class DataSource extends DataSourceProxy implements javax.sql.DataSource,MBeanRegistration, org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean, javax.sql.ConnectionPoolDataSource {
private static final Log log = LogFactory.getLog(DataSource.class);
/**
* Constructor for reflection only. A default set of pool properties will be created.
*/
public DataSource() {
super();
}
/**
* Constructs a DataSource object wrapping a connection
* @param poolProperties The pool properties
*/
public DataSource(PoolConfiguration poolProperties) {
super(poolProperties);
}
//===============================================================================
// JMX Operations - Register the actual pool itself under the tomcat.jdbc domain
//===============================================================================
protected volatile ObjectName oname = null;
/**
* Unregisters the underlying connection pool mbean.<br>
* {@inheritDoc}
*/
@Override
public void postDeregister() {
if (oname!=null) unregisterJmx();
}
/**
* no-op<br>
* {@inheritDoc}
*/
@Override
public void postRegister(Boolean registrationDone) {
// NOOP
}
/**
* no-op<br>
* {@inheritDoc}
*/
@Override
public void preDeregister() throws Exception {
// NOOP
}
/**
* If the connection pool MBean exists, it will be registered during this operation.<br>
* {@inheritDoc}
*/
@Override
public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
try {
if ( isJmxEnabled() ) {
this.oname = createObjectName(name);
if (oname!=null) registerJmx();
}
}catch (MalformedObjectNameException x) {
log.error("Unable to create object name for JDBC pool.",x);
}
return name;
}
/**
* Creates the ObjectName for the ConnectionPoolMBean object to be registered
* @param original the ObjectName for the DataSource
* @return the ObjectName for the ConnectionPoolMBean
* @throws MalformedObjectNameException Invalid object name
*/
public ObjectName createObjectName(ObjectName original) throws MalformedObjectNameException {
String domain = ConnectionPool.POOL_JMX_DOMAIN;
Hashtable<String,String> properties = original.getKeyPropertyList();
String origDomain = original.getDomain();
properties.put("type", "ConnectionPool");
properties.put("class", this.getClass().getName());
if (original.getKeyProperty("path")!=null || properties.get("context")!=null) {
//this ensures that if the registration came from tomcat, we're not losing
//the unique domain, but putting that into as an engine attribute
properties.put("engine", origDomain);
}
ObjectName name = new ObjectName(domain,properties);
return name;
}
/**
* Registers the ConnectionPoolMBean under a unique name based on the ObjectName for the DataSource
*/
protected void registerJmx() {
if (pool.getJmxPool()!=null) {
JmxUtil.registerJmx(oname, null, pool.getJmxPool());
}
}
/**
*
*/
protected void unregisterJmx() {
JmxUtil.unregisterJmx(oname);
}
}

View File

@@ -0,0 +1,598 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.sql.Connection;
import java.util.Hashtable;
import java.util.Properties;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import javax.sql.DataSource;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/**
* <p>JNDI object factory that creates an instance of
* <code>BasicDataSource</code> that has been configured based on the
* <code>RefAddr</code> values of the specified <code>Reference</code>,
* which must match the names and data types of the
* <code>BasicDataSource</code> bean properties.</p>
* <br>
* Properties available for configuration:<br>
* <a href="https://commons.apache.org/dbcp/configuration.html">Commons DBCP properties</a><br>
*<ol>
* <li>initSQL - A query that gets executed once, right after the connection is established.</li>
* <li>testOnConnect - run validationQuery after connection has been established.</li>
* <li>validationInterval - avoid excess validation, only run validation at most at this frequency - time in milliseconds.</li>
* <li>jdbcInterceptors - a semicolon separated list of classnames extending {@link JdbcInterceptor} class.</li>
* <li>jmxEnabled - true of false, whether to register the pool with JMX.</li>
* <li>fairQueue - true of false, whether the pool should sacrifice a little bit of performance for true fairness.</li>
*</ol>
* @author Craig R. McClanahan
* @author Dirk Verbeeck
*/
public class DataSourceFactory implements ObjectFactory {
private static final Log log = LogFactory.getLog(DataSourceFactory.class);
protected static final String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit";
protected static final String PROP_DEFAULTREADONLY = "defaultReadOnly";
protected static final String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation";
protected static final String PROP_DEFAULTCATALOG = "defaultCatalog";
protected static final String PROP_DRIVERCLASSNAME = "driverClassName";
protected static final String PROP_PASSWORD = "password";
protected static final String PROP_URL = "url";
protected static final String PROP_USERNAME = "username";
protected static final String PROP_MAXACTIVE = "maxActive";
protected static final String PROP_MAXIDLE = "maxIdle";
protected static final String PROP_MINIDLE = "minIdle";
protected static final String PROP_INITIALSIZE = "initialSize";
protected static final String PROP_MAXWAIT = "maxWait";
protected static final String PROP_MAXAGE = "maxAge";
protected static final String PROP_TESTONBORROW = "testOnBorrow";
protected static final String PROP_TESTONRETURN = "testOnReturn";
protected static final String PROP_TESTWHILEIDLE = "testWhileIdle";
protected static final String PROP_TESTONCONNECT = "testOnConnect";
protected static final String PROP_VALIDATIONQUERY = "validationQuery";
protected static final String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout";
protected static final String PROP_VALIDATOR_CLASS_NAME = "validatorClassName";
protected static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun";
protected static final String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis";
protected static final String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis";
protected static final String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed";
protected static final String PROP_REMOVEABANDONED = "removeAbandoned";
protected static final String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout";
protected static final String PROP_LOGABANDONED = "logAbandoned";
protected static final String PROP_ABANDONWHENPERCENTAGEFULL = "abandonWhenPercentageFull";
protected static final String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements";
protected static final String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements";
protected static final String PROP_CONNECTIONPROPERTIES = "connectionProperties";
protected static final String PROP_INITSQL = "initSQL";
protected static final String PROP_INTERCEPTORS = "jdbcInterceptors";
protected static final String PROP_VALIDATIONINTERVAL = "validationInterval";
protected static final String PROP_JMX_ENABLED = "jmxEnabled";
protected static final String PROP_FAIR_QUEUE = "fairQueue";
protected static final String PROP_USE_EQUALS = "useEquals";
protected static final String PROP_USE_CON_LOCK = "useLock";
protected static final String PROP_DATASOURCE= "dataSource";
protected static final String PROP_DATASOURCE_JNDI = "dataSourceJNDI";
protected static final String PROP_SUSPECT_TIMEOUT = "suspectTimeout";
protected static final String PROP_ALTERNATE_USERNAME_ALLOWED = "alternateUsernameAllowed";
protected static final String PROP_COMMITONRETURN = "commitOnReturn";
protected static final String PROP_ROLLBACKONRETURN = "rollbackOnReturn";
protected static final String PROP_USEDISPOSABLECONNECTIONFACADE = "useDisposableConnectionFacade";
protected static final String PROP_LOGVALIDATIONERRORS = "logValidationErrors";
protected static final String PROP_PROPAGATEINTERRUPTSTATE = "propagateInterruptState";
protected static final String PROP_IGNOREEXCEPTIONONPRELOAD = "ignoreExceptionOnPreLoad";
protected static final String PROP_USESTATEMENTFACADE = "useStatementFacade";
public static final int UNKNOWN_TRANSACTIONISOLATION = -1;
public static final String OBJECT_NAME = "object_name";
protected static final String[] ALL_PROPERTIES = {
PROP_DEFAULTAUTOCOMMIT,
PROP_DEFAULTREADONLY,
PROP_DEFAULTTRANSACTIONISOLATION,
PROP_DEFAULTCATALOG,
PROP_DRIVERCLASSNAME,
PROP_MAXACTIVE,
PROP_MAXIDLE,
PROP_MINIDLE,
PROP_INITIALSIZE,
PROP_MAXWAIT,
PROP_TESTONBORROW,
PROP_TESTONRETURN,
PROP_TIMEBETWEENEVICTIONRUNSMILLIS,
PROP_NUMTESTSPEREVICTIONRUN,
PROP_MINEVICTABLEIDLETIMEMILLIS,
PROP_TESTWHILEIDLE,
PROP_TESTONCONNECT,
PROP_PASSWORD,
PROP_URL,
PROP_USERNAME,
PROP_VALIDATIONQUERY,
PROP_VALIDATIONQUERY_TIMEOUT,
PROP_VALIDATOR_CLASS_NAME,
PROP_VALIDATIONINTERVAL,
PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED,
PROP_REMOVEABANDONED,
PROP_REMOVEABANDONEDTIMEOUT,
PROP_LOGABANDONED,
PROP_POOLPREPAREDSTATEMENTS,
PROP_MAXOPENPREPAREDSTATEMENTS,
PROP_CONNECTIONPROPERTIES,
PROP_INITSQL,
PROP_INTERCEPTORS,
PROP_JMX_ENABLED,
PROP_FAIR_QUEUE,
PROP_USE_EQUALS,
OBJECT_NAME,
PROP_ABANDONWHENPERCENTAGEFULL,
PROP_MAXAGE,
PROP_USE_CON_LOCK,
PROP_DATASOURCE,
PROP_DATASOURCE_JNDI,
PROP_SUSPECT_TIMEOUT,
PROP_ALTERNATE_USERNAME_ALLOWED,
PROP_COMMITONRETURN,
PROP_ROLLBACKONRETURN,
PROP_USEDISPOSABLECONNECTIONFACADE,
PROP_LOGVALIDATIONERRORS,
PROP_PROPAGATEINTERRUPTSTATE,
PROP_IGNOREEXCEPTIONONPRELOAD,
PROP_USESTATEMENTFACADE
};
// -------------------------------------------------- ObjectFactory Methods
/**
* <p>Create and return a new <code>BasicDataSource</code> instance. If no
* instance can be created, return <code>null</code> instead.</p>
*
* @param obj The possibly null object containing location or
* reference information that can be used in creating an object
* @param name The name of this object relative to <code>nameCtx</code>
* @param nameCtx The context relative to which the <code>name</code>
* parameter is specified, or <code>null</code> if <code>name</code>
* is relative to the default initial context
* @param environment The possibly null environment that is used in
* creating this object
*
* @exception Exception if an exception occurs creating the instance
*/
@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx,
Hashtable<?,?> environment) throws Exception {
// We only know how to deal with <code>javax.naming.Reference</code>s
// that specify a class name of "javax.sql.DataSource"
if ((obj == null) || !(obj instanceof Reference)) {
return null;
}
Reference ref = (Reference) obj;
boolean XA = false;
boolean ok = false;
if ("javax.sql.DataSource".equals(ref.getClassName())) {
ok = true;
}
if ("javax.sql.XADataSource".equals(ref.getClassName())) {
ok = true;
XA = true;
}
if (org.apache.tomcat.jdbc.pool.DataSource.class.getName().equals(ref.getClassName())) {
ok = true;
}
if (!ok) {
log.warn(ref.getClassName()+" is not a valid class name/type for this JNDI factory.");
return null;
}
Properties properties = new Properties();
for (int i = 0; i < ALL_PROPERTIES.length; i++) {
String propertyName = ALL_PROPERTIES[i];
RefAddr ra = ref.get(propertyName);
if (ra != null) {
String propertyValue = ra.getContent().toString();
properties.setProperty(propertyName, propertyValue);
}
}
return createDataSource(properties,nameCtx,XA);
}
public static PoolConfiguration parsePoolProperties(Properties properties) {
PoolConfiguration poolProperties = new PoolProperties();
String value = null;
value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT);
if (value != null) {
poolProperties.setDefaultAutoCommit(Boolean.valueOf(value));
}
value = properties.getProperty(PROP_DEFAULTREADONLY);
if (value != null) {
poolProperties.setDefaultReadOnly(Boolean.valueOf(value));
}
value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION);
if (value != null) {
int level = UNKNOWN_TRANSACTIONISOLATION;
if ("NONE".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_NONE;
} else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_READ_COMMITTED;
} else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_READ_UNCOMMITTED;
} else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_REPEATABLE_READ;
} else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_SERIALIZABLE;
} else {
try {
level = Integer.parseInt(value);
} catch (NumberFormatException e) {
System.err.println("Could not parse defaultTransactionIsolation: " + value);
System.err.println("WARNING: defaultTransactionIsolation not set");
System.err.println("using default value of database driver");
level = UNKNOWN_TRANSACTIONISOLATION;
}
}
poolProperties.setDefaultTransactionIsolation(level);
}
value = properties.getProperty(PROP_DEFAULTCATALOG);
if (value != null) {
poolProperties.setDefaultCatalog(value);
}
value = properties.getProperty(PROP_DRIVERCLASSNAME);
if (value != null) {
poolProperties.setDriverClassName(value);
}
value = properties.getProperty(PROP_MAXACTIVE);
if (value != null) {
poolProperties.setMaxActive(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MAXIDLE);
if (value != null) {
poolProperties.setMaxIdle(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MINIDLE);
if (value != null) {
poolProperties.setMinIdle(Integer.parseInt(value));
}
value = properties.getProperty(PROP_INITIALSIZE);
if (value != null) {
poolProperties.setInitialSize(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MAXWAIT);
if (value != null) {
poolProperties.setMaxWait(Integer.parseInt(value));
}
value = properties.getProperty(PROP_TESTONBORROW);
if (value != null) {
poolProperties.setTestOnBorrow(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_TESTONRETURN);
if (value != null) {
poolProperties.setTestOnReturn(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_TESTONCONNECT);
if (value != null) {
poolProperties.setTestOnConnect(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS);
if (value != null) {
poolProperties.setTimeBetweenEvictionRunsMillis(Integer.parseInt(value));
}
value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN);
if (value != null) {
poolProperties.setNumTestsPerEvictionRun(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS);
if (value != null) {
poolProperties.setMinEvictableIdleTimeMillis(Integer.parseInt(value));
}
value = properties.getProperty(PROP_TESTWHILEIDLE);
if (value != null) {
poolProperties.setTestWhileIdle(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_PASSWORD);
if (value != null) {
poolProperties.setPassword(value);
}
value = properties.getProperty(PROP_URL);
if (value != null) {
poolProperties.setUrl(value);
}
value = properties.getProperty(PROP_USERNAME);
if (value != null) {
poolProperties.setUsername(value);
}
value = properties.getProperty(PROP_VALIDATIONQUERY);
if (value != null) {
poolProperties.setValidationQuery(value);
}
value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT);
if (value != null) {
poolProperties.setValidationQueryTimeout(Integer.parseInt(value));
}
value = properties.getProperty(PROP_VALIDATOR_CLASS_NAME);
if (value != null) {
poolProperties.setValidatorClassName(value);
}
value = properties.getProperty(PROP_VALIDATIONINTERVAL);
if (value != null) {
poolProperties.setValidationInterval(Long.parseLong(value));
}
value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED);
if (value != null) {
poolProperties.setAccessToUnderlyingConnectionAllowed(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_REMOVEABANDONED);
if (value != null) {
poolProperties.setRemoveAbandoned(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT);
if (value != null) {
poolProperties.setRemoveAbandonedTimeout(Integer.parseInt(value));
}
value = properties.getProperty(PROP_LOGABANDONED);
if (value != null) {
poolProperties.setLogAbandoned(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS);
if (value != null) {
log.warn(PROP_POOLPREPAREDSTATEMENTS + " is not a valid setting, it will have no effect.");
}
value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS);
if (value != null) {
log.warn(PROP_MAXOPENPREPAREDSTATEMENTS + " is not a valid setting, it will have no effect.");
}
value = properties.getProperty(PROP_CONNECTIONPROPERTIES);
if (value != null) {
Properties p = getProperties(value);
poolProperties.setDbProperties(p);
} else {
poolProperties.setDbProperties(new Properties());
}
if (poolProperties.getUsername()!=null) {
poolProperties.getDbProperties().setProperty("user",poolProperties.getUsername());
}
if (poolProperties.getPassword()!=null) {
poolProperties.getDbProperties().setProperty("password",poolProperties.getPassword());
}
value = properties.getProperty(PROP_INITSQL);
if (value != null) {
poolProperties.setInitSQL(value);
}
value = properties.getProperty(PROP_INTERCEPTORS);
if (value != null) {
poolProperties.setJdbcInterceptors(value);
}
value = properties.getProperty(PROP_JMX_ENABLED);
if (value != null) {
poolProperties.setJmxEnabled(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_FAIR_QUEUE);
if (value != null) {
poolProperties.setFairQueue(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_USE_EQUALS);
if (value != null) {
poolProperties.setUseEquals(Boolean.parseBoolean(value));
}
value = properties.getProperty(OBJECT_NAME);
if (value != null) {
poolProperties.setName(ObjectName.quote(value));
}
value = properties.getProperty(PROP_ABANDONWHENPERCENTAGEFULL);
if (value != null) {
poolProperties.setAbandonWhenPercentageFull(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MAXAGE);
if (value != null) {
poolProperties.setMaxAge(Long.parseLong(value));
}
value = properties.getProperty(PROP_USE_CON_LOCK);
if (value != null) {
poolProperties.setUseLock(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_DATASOURCE);
if (value != null) {
//this should never happen
throw new IllegalArgumentException("Can't set dataSource property as a string, this must be a javax.sql.DataSource object.");
}
value = properties.getProperty(PROP_DATASOURCE_JNDI);
if (value != null) {
poolProperties.setDataSourceJNDI(value);
}
value = properties.getProperty(PROP_SUSPECT_TIMEOUT);
if (value != null) {
poolProperties.setSuspectTimeout(Integer.parseInt(value));
}
value = properties.getProperty(PROP_ALTERNATE_USERNAME_ALLOWED);
if (value != null) {
poolProperties.setAlternateUsernameAllowed(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_COMMITONRETURN);
if (value != null) {
poolProperties.setCommitOnReturn(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_ROLLBACKONRETURN);
if (value != null) {
poolProperties.setRollbackOnReturn(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_USEDISPOSABLECONNECTIONFACADE);
if (value != null) {
poolProperties.setUseDisposableConnectionFacade(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_LOGVALIDATIONERRORS);
if (value != null) {
poolProperties.setLogValidationErrors(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_PROPAGATEINTERRUPTSTATE);
if (value != null) {
poolProperties.setPropagateInterruptState(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_IGNOREEXCEPTIONONPRELOAD);
if (value != null) {
poolProperties.setIgnoreExceptionOnPreLoad(Boolean.parseBoolean(value));
}
value = properties.getProperty(PROP_USESTATEMENTFACADE);
if (value != null) {
poolProperties.setUseStatementFacade(Boolean.parseBoolean(value));
}
return poolProperties;
}
/**
* Creates and configures a {@link DataSource} instance based on the
* given properties.
*
* @param properties the datasource configuration properties
* @return the datasource
* @throws Exception if an error occurs creating the data source
*/
public DataSource createDataSource(Properties properties) throws Exception {
return createDataSource(properties,null,false);
}
public DataSource createDataSource(Properties properties,Context context, boolean XA) throws Exception {
PoolConfiguration poolProperties = DataSourceFactory.parsePoolProperties(properties);
if (poolProperties.getDataSourceJNDI()!=null && poolProperties.getDataSource()==null) {
performJNDILookup(context, poolProperties);
}
org.apache.tomcat.jdbc.pool.DataSource dataSource = XA?
new org.apache.tomcat.jdbc.pool.XADataSource(poolProperties) :
new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
//initialise the pool itself
dataSource.createPool();
// Return the configured DataSource instance
return dataSource;
}
public void performJNDILookup(Context context, PoolConfiguration poolProperties) {
Object jndiDS = null;
try {
if (context!=null) {
jndiDS = context.lookup(poolProperties.getDataSourceJNDI());
} else {
log.warn("dataSourceJNDI property is configured, but local JNDI context is null.");
}
} catch (NamingException e) {
log.debug("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the local context.");
}
if (jndiDS==null) {
try {
context = new InitialContext();
jndiDS = context.lookup(poolProperties.getDataSourceJNDI());
} catch (NamingException e) {
log.warn("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the InitialContext.");
}
}
if (jndiDS!=null) {
poolProperties.setDataSource(jndiDS);
}
}
/**
* Parse properties from the string. Format of the string must be [propertyName=property;]*.
* @param propText The properties string
* @return the properties
*/
protected static Properties getProperties(String propText) {
return PoolProperties.getProperties(propText,null);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,98 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.SQLException;
/**
* A DisposableConnectionFacade object is the top most interceptor that wraps an
* object of type {@link PooledConnection}. The DisposableConnectionFacade intercepts
* two methods:
* <ul>
* <li>{@link java.sql.Connection#close()} - returns the connection to the
* pool then breaks the link between cutoff and the next interceptor.
* May be called multiple times.</li>
* <li>{@link java.lang.Object#toString()} - returns a custom string for this
* object</li>
* </ul>
* By default method comparisons is done on a String reference level, unless the
* {@link PoolConfiguration#setUseEquals(boolean)} has been called with a
* <code>true</code> argument.
*/
public class DisposableConnectionFacade extends JdbcInterceptor {
protected DisposableConnectionFacade(JdbcInterceptor interceptor) {
setUseEquals(interceptor.isUseEquals());
setNext(interceptor);
}
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return this==obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (compare(EQUALS_VAL, method)) {
return Boolean.valueOf(
this.equals(Proxy.getInvocationHandler(args[0])));
} else if (compare(HASHCODE_VAL, method)) {
return Integer.valueOf(this.hashCode());
} else if (getNext()==null) {
if (compare(ISCLOSED_VAL, method)) {
return Boolean.TRUE;
}
else if (compare(CLOSE_VAL, method)) {
return null;
}
else if (compare(ISVALID_VAL, method)) {
return Boolean.FALSE;
}
}
try {
return super.invoke(proxy, method, args);
} catch (NullPointerException e) {
if (getNext() == null) {
if (compare(TOSTRING_VAL, method)) {
return "DisposableConnectionFacade[null]";
}
throw new SQLException(
"PooledConnection has already been closed.");
}
throw e;
} finally {
if (compare(CLOSE_VAL, method)) {
setNext(null);
}
}
}
}

View File

@@ -0,0 +1,559 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* A simple implementation of a blocking queue with fairness waiting.
* invocations to method poll(...) will get handed out in the order they were received.
* Locking is fine grained, a shared lock is only used during the first level of contention, waiting is done in a
* lock per thread basis so that order is guaranteed once the thread goes into a suspended monitor state.
* <br>
* Not all of the methods of the {@link java.util.concurrent.BlockingQueue} are implemented.
*
* @param <E> Type of element in the queue
*/
public class FairBlockingQueue<E> implements BlockingQueue<E> {
/**
* This little sucker is used to reorder the way to do
* {@link java.util.concurrent.locks.Lock#lock()},
* {@link java.util.concurrent.locks.Lock#unlock()}
* and
* {@link java.util.concurrent.CountDownLatch#countDown()}
* during the {@link #poll(long, TimeUnit)} operation.
* On Linux, it performs much better if we count down while we hold the global
* lock, on Solaris its the other way around.
* Until we have tested other platforms we only check for Linux.
*/
static final boolean isLinux = "Linux".equals(System.getProperty("os.name")) &&
(!Boolean.getBoolean(FairBlockingQueue.class.getName()+".ignoreOS"));
/**
* Phase one entry lock in order to give out
* per-thread-locks for the waiting phase we have
* a phase one lock during the contention period.
*/
final ReentrantLock lock = new ReentrantLock(false);
/**
* All the objects in the pool are stored in a simple linked list
*/
final LinkedList<E> items;
/**
* All threads waiting for an object are stored in a linked list
*/
final LinkedList<ExchangeCountDownLatch<E>> waiters;
/**
* Creates a new fair blocking queue.
*/
public FairBlockingQueue() {
items = new LinkedList<>();
waiters = new LinkedList<>();
}
//------------------------------------------------------------------
// USED BY CONPOOL IMPLEMENTATION
//------------------------------------------------------------------
/**
* Will always return true, queue is unbounded.
* {@inheritDoc}
*/
@Override
public boolean offer(E e) {
//during the offer, we will grab the main lock
final ReentrantLock lock = this.lock;
lock.lock();
ExchangeCountDownLatch<E> c = null;
try {
//check to see if threads are waiting for an object
if (!waiters.isEmpty()) {
//if threads are waiting grab the latch for that thread
c = waiters.poll();
//give the object to the thread instead of adding it to the pool
c.setItem(e);
if (isLinux) c.countDown();
} else {
//we always add first, so that the most recently used object will be given out
items.addFirst(e);
}
} finally {
lock.unlock();
}
//if we exchanged an object with another thread, wake it up.
if (!isLinux && c!=null) c.countDown();
//we have an unbounded queue, so always return true
return true;
}
/**
* Will never timeout, as it invokes the {@link #offer(Object)} method.
* Once a lock has been acquired, the
* {@inheritDoc}
*/
@Override
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
return offer(e);
}
/**
* Fair retrieval of an object in the queue.
* Objects are returned in the order the threads requested them.
* {@inheritDoc}
*/
@Override
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E result = null;
final ReentrantLock lock = this.lock;
//acquire the global lock until we know what to do
lock.lock();
try {
//check to see if we have objects
result = items.poll();
if (result==null && timeout>0) {
//the queue is empty we will wait for an object
ExchangeCountDownLatch<E> c = new ExchangeCountDownLatch<>(1);
//add to the bottom of the wait list
waiters.addLast(c);
//unlock the global lock
lock.unlock();
boolean didtimeout = true;
InterruptedException interruptedException = null;
try {
//wait for the specified timeout
didtimeout = !c.await(timeout, unit);
} catch (InterruptedException ix) {
interruptedException = ix;
}
if (didtimeout) {
//if we timed out, or got interrupted
// remove ourselves from the waitlist
lock.lock();
try {
waiters.remove(c);
} finally {
lock.unlock();
}
}
//return the item we received, can be null if we timed out
result = c.getItem();
if (null!=interruptedException) {
//we got interrupted
if ( null!=result) {
//we got a result - clear the interrupt status
//don't propagate cause we have removed a connection from pool
Thread.interrupted();
} else {
throw interruptedException;
}
}
} else {
//we have an object, release
lock.unlock();
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return result;
}
/**
* Request an item from the queue asynchronously
* @return - a future pending the result from the queue poll request
*/
public Future<E> pollAsync() {
Future<E> result = null;
final ReentrantLock lock = this.lock;
//grab the global lock
lock.lock();
try {
//check to see if we have objects in the queue
E item = items.poll();
if (item==null) {
//queue is empty, add ourselves as waiters
ExchangeCountDownLatch<E> c = new ExchangeCountDownLatch<>(1);
waiters.addLast(c);
//return a future that will wait for the object
result = new ItemFuture<>(c);
} else {
//return a future with the item
result = new ItemFuture<>(item);
}
} finally {
lock.unlock();
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean remove(Object e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return items.remove(e);
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public int size() {
return items.size();
}
/**
* {@inheritDoc}
*/
@Override
public Iterator<E> iterator() {
return new FairIterator();
}
/**
* {@inheritDoc}
*/
@Override
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return items.poll();
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean contains(Object e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return items.contains(e);
} finally {
lock.unlock();
}
}
//------------------------------------------------------------------
// NOT USED BY CONPOOL IMPLEMENTATION
//------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public boolean add(E e) {
return offer(e);
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public int drainTo(Collection<? super E> c, int maxElements) {
throw new UnsupportedOperationException("int drainTo(Collection<? super E> c, int maxElements)");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public int drainTo(Collection<? super E> c) {
return drainTo(c,Integer.MAX_VALUE);
}
/**
* {@inheritDoc}
*/
@Override
public void put(E e) throws InterruptedException {
offer(e);
}
/**
* {@inheritDoc}
*/
@Override
public int remainingCapacity() {
return Integer.MAX_VALUE - size();
}
/**
* {@inheritDoc}
*/
@Override
public E take() throws InterruptedException {
return this.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
/**
* {@inheritDoc}
*/
@Override
public boolean addAll(Collection<? extends E> c) {
Iterator<? extends E> i = c.iterator();
while (i.hasNext()) {
E e = i.next();
offer(e);
}
return true;
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public void clear() {
throw new UnsupportedOperationException("void clear()");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public boolean containsAll(Collection<?> c) {
throw new UnsupportedOperationException("boolean containsAll(Collection<?> c)");
}
/**
* {@inheritDoc}
*/
@Override
public boolean isEmpty() {
return size() == 0;
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException("boolean removeAll(Collection<?> c)");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException("boolean retainAll(Collection<?> c)");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public Object[] toArray() {
throw new UnsupportedOperationException("Object[] toArray()");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public <T> T[] toArray(T[] a) {
throw new UnsupportedOperationException("<T> T[] toArray(T[] a)");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public E element() {
throw new UnsupportedOperationException("E element()");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public E peek() {
throw new UnsupportedOperationException("E peek()");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public E remove() {
throw new UnsupportedOperationException("E remove()");
}
//------------------------------------------------------------------
// Non cancellable Future used to check and see if a connection has been made available
//------------------------------------------------------------------
protected class ItemFuture<T> implements Future<T> {
protected volatile T item = null;
protected volatile ExchangeCountDownLatch<T> latch = null;
protected volatile boolean canceled = false;
public ItemFuture(T item) {
this.item = item;
}
public ItemFuture(ExchangeCountDownLatch<T> latch) {
this.latch = latch;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false; //don't allow cancel for now
}
@Override
public T get() throws InterruptedException, ExecutionException {
if (item!=null) {
return item;
} else if (latch!=null) {
latch.await();
return latch.getItem();
} else {
throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception());
}
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (item!=null) {
return item;
} else if (latch!=null) {
boolean timedout = !latch.await(timeout, unit);
if (timedout) throw new TimeoutException();
else return latch.getItem();
} else {
throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception());
}
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return (item!=null || latch.getItem()!=null);
}
}
//------------------------------------------------------------------
// Count down latch that can be used to exchange information
//------------------------------------------------------------------
protected class ExchangeCountDownLatch<T> extends CountDownLatch {
protected volatile T item;
public ExchangeCountDownLatch(int i) {
super(i);
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
//------------------------------------------------------------------
// Iterator safe from concurrent modification exceptions
//------------------------------------------------------------------
protected class FairIterator implements Iterator<E> {
E[] elements = null;
int index;
E element = null;
@SuppressWarnings("unchecked") // Can't create arrays of generic types
public FairIterator() {
final ReentrantLock lock = FairBlockingQueue.this.lock;
lock.lock();
try {
elements = (E[]) new Object[FairBlockingQueue.this.items.size()];
FairBlockingQueue.this.items.toArray(elements);
index = 0;
} finally {
lock.unlock();
}
}
@Override
public boolean hasNext() {
return index<elements.length;
}
@Override
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
element = elements[index++];
return element;
}
@Override
public void remove() {
final ReentrantLock lock = FairBlockingQueue.this.lock;
lock.lock();
try {
if (element!=null) {
FairBlockingQueue.this.items.remove(element);
}
} finally {
lock.unlock();
}
}
}
}

View File

@@ -0,0 +1,239 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
/**
* Abstract class that is to be extended for implementations of interceptors.
* Everytime an operation is called on the {@link java.sql.Connection} object the
* {@link #invoke(Object, Method, Object[])} method on the interceptor will be called.
* Interceptors are useful to change or improve behavior of the connection pool.<br>
* Interceptors can receive a set of properties. Each sub class is responsible for parsing the properties during runtime when they
* are needed or simply override the {@link #setProperties(Map)} method.
* Properties arrive in a key-value pair of Strings as they were received through the configuration.
* This method is called once per cached connection object when the object is first configured.
*
* @version 1.0
*/
public abstract class JdbcInterceptor implements InvocationHandler {
/**
* {@link java.sql.Connection#close()} method name
*/
public static final String CLOSE_VAL = "close";
/**
* {@link Object#toString()} method name
*/
public static final String TOSTRING_VAL = "toString";
/**
* {@link java.sql.Connection#isClosed()} method name
*/
public static final String ISCLOSED_VAL = "isClosed";
/**
* {@link javax.sql.PooledConnection#getConnection()} method name
*/
public static final String GETCONNECTION_VAL = "getConnection";
/**
* {@link java.sql.Wrapper#unwrap(Class)} method name
*/
public static final String UNWRAP_VAL = "unwrap";
/**
* {@link java.sql.Wrapper#isWrapperFor(Class)} method name
*/
public static final String ISWRAPPERFOR_VAL = "isWrapperFor";
/**
* {@link java.sql.Connection#isValid(int)} method name
*/
public static final String ISVALID_VAL = "isValid";
/**
* {@link java.lang.Object#equals(Object)}
*/
public static final String EQUALS_VAL = "equals";
/**
* {@link java.lang.Object#hashCode()}
*/
public static final String HASHCODE_VAL = "hashCode";
/**
* Properties for this interceptor.
*/
protected Map<String,InterceptorProperty> properties = null;
/**
* The next interceptor in the chain
*/
private volatile JdbcInterceptor next = null;
/**
* Property that decides how we do string comparison, default is to use
* {@link String#equals(Object)}. If set to <code>false</code> then the
* equality operator (==) is used.
*/
private boolean useEquals = true;
/**
* Public constructor for instantiation through reflection
*/
public JdbcInterceptor() {
// NOOP
}
/**
* Gets invoked each time an operation on {@link java.sql.Connection} is invoked.
* {@inheritDoc}
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (getNext()!=null) return getNext().invoke(proxy,method,args);
else throw new NullPointerException();
}
/**
* Returns the next interceptor in the chain
* @return the next interceptor in the chain
*/
public JdbcInterceptor getNext() {
return next;
}
/**
* configures the next interceptor in the chain
* @param next The next chain item
*/
public void setNext(JdbcInterceptor next) {
this.next = next;
}
/**
* Performs a string comparison, using references unless the useEquals property is set to true.
* @param name1 The first name
* @param name2 The second name
* @return true if name1 is equal to name2 based on {@link #useEquals}
*/
public boolean compare(String name1, String name2) {
if (isUseEquals()) {
return name1.equals(name2);
} else {
return name1==name2;
}
}
/**
* Compares a method name (String) to a method (Method)
* {@link #compare(String,String)}
* Uses reference comparison unless the useEquals property is set to true
* @param methodName The method name
* @param method The method
* @return <code>true</code> if the name matches
*/
public boolean compare(String methodName, Method method) {
return compare(methodName, method.getName());
}
/**
* Gets called each time the connection is borrowed from the pool
* This means that if an interceptor holds a reference to the connection
* the interceptor can be reused for another connection.
* <br>
* This method may be called with null as both arguments when we are closing down the connection.
* @param parent - the connection pool owning the connection
* @param con - the pooled connection
*/
public abstract void reset(ConnectionPool parent, PooledConnection con);
/**
* Called when {@link java.sql.Connection#close()} is called on the underlying connection.
* This is to notify the interceptors, that the physical connection has been released.
* Implementation of this method should be thought through with care, as no actions should trigger an exception.
* @param parent - the connection pool that this connection belongs to
* @param con - the pooled connection that holds this connection
* @param finalizing - if this connection is finalizing. True means that the pooled connection will not reconnect the underlying connection
*/
public void disconnected(ConnectionPool parent, PooledConnection con, boolean finalizing) {
}
/**
* Returns the properties configured for this interceptor
* @return the configured properties for this interceptor
*/
public Map<String,InterceptorProperty> getProperties() {
return properties;
}
/**
* Called during the creation of an interceptor
* The properties can be set during the configuration of an interceptor
* Override this method to perform type casts between string values and object properties
* @param properties The properties
*/
public void setProperties(Map<String,InterceptorProperty> properties) {
this.properties = properties;
final String useEquals = "useEquals";
InterceptorProperty p = properties.get(useEquals);
if (p!=null) {
setUseEquals(Boolean.parseBoolean(p.getValue()));
}
}
/**
* @return true if the compare method uses the Object.equals(Object) method
* false if comparison is done on a reference level
*/
public boolean isUseEquals() {
return useEquals;
}
/**
* Set to true if string comparisons (for the {@link #compare(String, Method)} and {@link #compare(String, String)} methods) should use the Object.equals(Object) method
* The default is false
* @param useEquals <code>true</code> if equals will be used for comparisons
*/
public void setUseEquals(boolean useEquals) {
this.useEquals = useEquals;
}
/**
* This method is invoked by a connection pool when the pool is closed.
* Interceptor classes can override this method if they keep static
* variables or other tracking means around.
* <b>This method is only invoked on a single instance of the interceptor, and not on every instance created.</b>
* @param pool - the pool that is being closed.
*/
public void poolClosed(ConnectionPool pool) {
// NOOP
}
/**
* This method is invoked by a connection pool when the pool is first started up, usually when the first connection is requested.
* Interceptor classes can override this method if they keep static
* variables or other tracking means around.
* <b>This method is only invoked on a single instance of the interceptor, and not on every instance created.</b>
* @param pool - the pool that is being closed.
*/
public void poolStarted(ConnectionPool pool) {
// NOOP
}
}

View File

@@ -0,0 +1,564 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
/**
* <b>EXPERIMENTAL AND NOT YET COMPLETE!</b>
*
*
* An implementation of a blocking queue with fairness waiting and lock dispersal to avoid contention.
* invocations to method poll(...) will get handed out in the order they were received.
* Locking is fine grained, a shared lock is only used during the first level of contention, waiting is done in a
* lock per thread basis so that order is guaranteed once the thread goes into a suspended monitor state.
* <br>
* Not all of the methods of the {@link java.util.concurrent.BlockingQueue} are implemented.
*
* @param <E> Type of element in the queue
*/
public class MultiLockFairBlockingQueue<E> implements BlockingQueue<E> {
final int LOCK_COUNT = Runtime.getRuntime().availableProcessors();
final AtomicInteger putQueue = new AtomicInteger(0);
final AtomicInteger pollQueue = new AtomicInteger(0);
public int getNextPut() {
int idx = Math.abs(putQueue.incrementAndGet()) % LOCK_COUNT;
return idx;
}
public int getNextPoll() {
int idx = Math.abs(pollQueue.incrementAndGet()) % LOCK_COUNT;
return idx;
}
/**
* Phase one entry lock in order to give out
* per-thread-locks for the waiting phase we have
* a phase one lock during the contention period.
*/
private final ReentrantLock[] locks = new ReentrantLock[LOCK_COUNT];
/**
* All the objects in the pool are stored in a simple linked list
*/
final LinkedList<E>[] items;
/**
* All threads waiting for an object are stored in a linked list
*/
final LinkedList<ExchangeCountDownLatch<E>>[] waiters;
/**
* Creates a new fair blocking queue.
*/
@SuppressWarnings("unchecked") // Can create arrays of generic types
public MultiLockFairBlockingQueue() {
items = new LinkedList[LOCK_COUNT];
waiters = new LinkedList[LOCK_COUNT];
for (int i=0; i<LOCK_COUNT; i++) {
items[i] = new LinkedList<>();
waiters[i] = new LinkedList<>();
locks[i] = new ReentrantLock(false);
}
}
//------------------------------------------------------------------
// USED BY CONPOOL IMPLEMENTATION
//------------------------------------------------------------------
/**
* Will always return true, queue is unbounded.
* {@inheritDoc}
*/
@Override
public boolean offer(E e) {
int idx = getNextPut();
//during the offer, we will grab the main lock
final ReentrantLock lock = this.locks[idx];
lock.lock();
ExchangeCountDownLatch<E> c = null;
try {
//check to see if threads are waiting for an object
if (!waiters[idx].isEmpty()) {
//if threads are waiting grab the latch for that thread
c = waiters[idx].poll();
//give the object to the thread instead of adding it to the pool
c.setItem(e);
} else {
//we always add first, so that the most recently used object will be given out
items[idx].addFirst(e);
}
} finally {
lock.unlock();
}
//if we exchanged an object with another thread, wake it up.
if (c!=null) c.countDown();
//we have an unbounded queue, so always return true
return true;
}
/**
* Will never timeout, as it invokes the {@link #offer(Object)} method.
* Once a lock has been acquired, the
* {@inheritDoc}
*/
@Override
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
return offer(e);
}
/**
* Fair retrieval of an object in the queue.
* Objects are returned in the order the threads requested them.
* {@inheritDoc}
*/
@Override
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
int idx = getNextPoll();
E result = null;
final ReentrantLock lock = this.locks[idx];
try {
//acquire the global lock until we know what to do
lock.lock();
//check to see if we have objects
result = items[idx].poll();
if (result==null && timeout>0) {
//the queue is empty we will wait for an object
ExchangeCountDownLatch<E> c = new ExchangeCountDownLatch<>(1);
//add to the bottom of the wait list
waiters[idx].addLast(c);
//unlock the global lock
lock.unlock();
//wait for the specified timeout
if (!c.await(timeout, unit)) {
//if we timed out, remove ourselves from the waitlist
lock.lock();
waiters[idx].remove(c);
lock.unlock();
}
//return the item we received, can be null if we timed out
result = c.getItem();
} else {
//we have an object, release
lock.unlock();
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return result;
}
/**
* Request an item from the queue asynchronously
* @return - a future pending the result from the queue poll request
*/
public Future<E> pollAsync() {
int idx = getNextPoll();
Future<E> result = null;
final ReentrantLock lock = this.locks[idx];
try {
//grab the global lock
lock.lock();
//check to see if we have objects in the queue
E item = items[idx].poll();
if (item==null) {
//queue is empty, add ourselves as waiters
ExchangeCountDownLatch<E> c = new ExchangeCountDownLatch<>(1);
waiters[idx].addLast(c);
//return a future that will wait for the object
result = new ItemFuture<>(c);
} else {
//return a future with the item
result = new ItemFuture<>(item);
}
} finally {
lock.unlock();
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean remove(Object e) {
for (int idx=0; idx<LOCK_COUNT; idx++) {
final ReentrantLock lock = this.locks[idx];
lock.lock();
try {
boolean result = items[idx].remove(e);
if (result) return result;
} finally {
lock.unlock();
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public int size() {
int size = 0;
for (int idx=0; idx<LOCK_COUNT; idx++) {
size += items[idx].size();
}
return size;
}
/**
* {@inheritDoc}
*/
@Override
public Iterator<E> iterator() {
return new FairIterator();
}
/**
* {@inheritDoc}
*/
@Override
public E poll() {
int idx = getNextPoll();
final ReentrantLock lock = this.locks[idx];
lock.lock();
try {
return items[idx].poll();
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean contains(Object e) {
for (int idx=0; idx<LOCK_COUNT; idx++) {
boolean result = items[idx].contains(e);
if (result) return result;
}
return false;
}
//------------------------------------------------------------------
// NOT USED BY CONPOOL IMPLEMENTATION
//------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public boolean add(E e) {
return offer(e);
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public int drainTo(Collection<? super E> c, int maxElements) {
throw new UnsupportedOperationException("int drainTo(Collection<? super E> c, int maxElements)");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public int drainTo(Collection<? super E> c) {
return drainTo(c,Integer.MAX_VALUE);
}
/**
* {@inheritDoc}
*/
@Override
public void put(E e) throws InterruptedException {
offer(e);
}
/**
* {@inheritDoc}
*/
@Override
public int remainingCapacity() {
return Integer.MAX_VALUE - size();
}
/**
* {@inheritDoc}
*/
@Override
public E take() throws InterruptedException {
return this.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
/**
* {@inheritDoc}
*/
@Override
public boolean addAll(Collection<? extends E> c) {
Iterator<? extends E> i = c.iterator();
while (i.hasNext()) {
E e = i.next();
offer(e);
}
return true;
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public void clear() {
throw new UnsupportedOperationException("void clear()");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public boolean containsAll(Collection<?> c) {
throw new UnsupportedOperationException("boolean containsAll(Collection<?> c)");
}
/**
* {@inheritDoc}
*/
@Override
public boolean isEmpty() {
return size() == 0;
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException("boolean removeAll(Collection<?> c)");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException("boolean retainAll(Collection<?> c)");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public Object[] toArray() {
throw new UnsupportedOperationException("Object[] toArray()");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public <T> T[] toArray(T[] a) {
throw new UnsupportedOperationException("<T> T[] toArray(T[] a)");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public E element() {
throw new UnsupportedOperationException("E element()");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public E peek() {
throw new UnsupportedOperationException("E peek()");
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException - this operation is not supported
*/
@Override
public E remove() {
throw new UnsupportedOperationException("E remove()");
}
//------------------------------------------------------------------
// Non cancellable Future used to check and see if a connection has been made available
//------------------------------------------------------------------
protected class ItemFuture<T> implements Future<T> {
protected volatile T item = null;
protected volatile ExchangeCountDownLatch<T> latch = null;
protected volatile boolean canceled = false;
public ItemFuture(T item) {
this.item = item;
}
public ItemFuture(ExchangeCountDownLatch<T> latch) {
this.latch = latch;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false; //don't allow cancel for now
}
@Override
public T get() throws InterruptedException, ExecutionException {
if (item!=null) {
return item;
} else if (latch!=null) {
latch.await();
return latch.getItem();
} else {
throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception());
}
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (item!=null) {
return item;
} else if (latch!=null) {
boolean timedout = !latch.await(timeout, unit);
if (timedout) throw new TimeoutException();
else return latch.getItem();
} else {
throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception());
}
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return (item!=null || latch.getItem()!=null);
}
}
//------------------------------------------------------------------
// Count down latch that can be used to exchange information
//------------------------------------------------------------------
protected class ExchangeCountDownLatch<T> extends CountDownLatch {
protected volatile T item;
public ExchangeCountDownLatch(int i) {
super(i);
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
//------------------------------------------------------------------
// Iterator safe from concurrent modification exceptions
//------------------------------------------------------------------
protected class FairIterator implements Iterator<E> {
E[] elements = null;
int index;
E element = null;
@SuppressWarnings("unchecked") // Can't create arrays of generic types
public FairIterator() {
ArrayList<E> list = new ArrayList<>(MultiLockFairBlockingQueue.this.size());
for (int idx=0; idx<LOCK_COUNT; idx++) {
final ReentrantLock lock = MultiLockFairBlockingQueue.this.locks[idx];
lock.lock();
try {
elements = (E[]) new Object[MultiLockFairBlockingQueue.this.items[idx].size()];
MultiLockFairBlockingQueue.this.items[idx].toArray(elements);
} finally {
lock.unlock();
}
}
index = 0;
elements = (E[]) new Object[list.size()];
list.toArray(elements);
}
@Override
public boolean hasNext() {
return index<elements.length;
}
@Override
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
element = elements[index++];
return element;
}
@Override
public void remove() {
for (int idx=0; idx<LOCK_COUNT; idx++) {
final ReentrantLock lock = MultiLockFairBlockingQueue.this.locks[idx];
lock.lock();
try {
boolean result = MultiLockFairBlockingQueue.this.items[idx].remove(elements[index]);
if (result) break;
} finally {
lock.unlock();
}
}
}
}
}

View File

@@ -0,0 +1,913 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.util.Properties;
import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorDefinition;
/**
* A list of properties that are configurable for a connection pool.
* The {@link DataSource} object also implements this interface so that it can be easily configured through
* an IoC container without having to specify a secondary object with a setter method.
*
*/
public interface PoolConfiguration {
/**
* JMX prefix for interceptors that register themselves with JMX
*/
public static final String PKG_PREFIX = "org.apache.tomcat.jdbc.pool.interceptor.";
/**
* Connections that have been abandoned (timed out) wont get closed and reported up unless the number of connections in use are
* above the percentage defined by abandonWhenPercentageFull.
* The value should be between 0-100.
* The default value is 0, which implies that connections are eligible for
* closure as soon as removeAbandonedTimeout has been reached.
* @param percentage a value between 0 and 100 to indicate when connections that have been abandoned/timed out are considered abandoned
*/
public void setAbandonWhenPercentageFull(int percentage);
/**
* Connections that have been abandoned (timed out) wont get closed and reported up unless the number of connections in use are
* above the percentage defined by abandonWhenPercentageFull.
* The value should be between 0-100.
* The default value is 0, which implies that connections are eligible for
* closure as soon as removeAbandonedTimeout has been reached.
* @return percentage - a value between 0 and 100 to indicate when connections that have been abandoned/timed out are considered abandoned
*/
public int getAbandonWhenPercentageFull();
/**
* Returns <code>true</code> if a fair queue is being used by the connection pool
* @return <code>true</code> if a fair waiting queue is being used
*/
public boolean isFairQueue();
/**
* Set to true if you wish that calls to getConnection
* should be treated fairly in a true FIFO fashion.
* This uses the {@link FairBlockingQueue} implementation for the list of the idle connections.
* The default value is true.
* This flag is required when you want to use asynchronous connection retrieval.
* @param fairQueue <code>true</code> to use a fair queue
*/
public void setFairQueue(boolean fairQueue);
/**
* Property not used. Access is always allowed.
* Access can be achieved by calling unwrap on the pooled connection. see {@link javax.sql.DataSource} interface
* or call getConnection through reflection or cast the object as {@link javax.sql.PooledConnection}
* @return <code>true</code>
*/
public boolean isAccessToUnderlyingConnectionAllowed();
/**
* No-op
* @param accessToUnderlyingConnectionAllowed parameter ignored
*/
public void setAccessToUnderlyingConnectionAllowed(boolean accessToUnderlyingConnectionAllowed);
/**
* The connection properties that will be sent to the JDBC driver when establishing new connections.
* Format of the string is [propertyName=property;] <br>
* NOTE - The "user" and "password" properties will be passed explicitly, so they do not need to be included here.
* The default value is null.
* @return the connection properties
*/
public String getConnectionProperties();
/**
* The properties that will be passed into {@link java.sql.Driver#connect(String, Properties)} method.
* Username and password do not need to be stored here, they will be passed into the properties right before the connection is established.
* @param connectionProperties properties - Format of the string is [propertyName=property;]*
* Example: prop1=value1;prop2=value2
*/
public void setConnectionProperties(String connectionProperties);
/**
* Returns the database properties that are passed into the {@link java.sql.Driver#connect(String, Properties)} method.
* @return database properties that are passed into the {@link java.sql.Driver#connect(String, Properties)} method.
*/
public Properties getDbProperties();
/**
* Overrides the database properties passed into the {@link java.sql.Driver#connect(String, Properties)} method.
* @param dbProperties The database properties
*/
public void setDbProperties(Properties dbProperties);
/**
* The default auto-commit state of connections created by this pool.
* If not set (null), default is JDBC driver default (If set to null then the {@link java.sql.Connection#setAutoCommit(boolean)} method will not be called.)
* @return the default auto commit setting, null is Driver default.
*/
public Boolean isDefaultAutoCommit();
/**
* The default auto-commit state of connections created by this pool.
* If not set (null), default is JDBC driver default (If set to null then the {@link java.sql.Connection#setAutoCommit(boolean)} method will not be called.)
* @return the default auto commit setting, null is Driver default.
*/
public Boolean getDefaultAutoCommit();
/**
* The default auto-commit state of connections created by this pool.
* If not set (null), default is JDBC driver default (If set to null then the {@link java.sql.Connection#setAutoCommit(boolean)} method will not be called.)
* @param defaultAutoCommit default auto commit setting, null is Driver default.
*/
public void setDefaultAutoCommit(Boolean defaultAutoCommit);
/**
* If non null, during connection creation the method {@link java.sql.Connection#setCatalog(String)} will be called with the set value.
* @return the default catalog, null if not set and accepting the driver default.
*/
public String getDefaultCatalog();
/**
* If non null, during connection creation the method {@link java.sql.Connection#setCatalog(String)} will be called with the set value.
* @param defaultCatalog null if not set and accepting the driver default.
*/
public void setDefaultCatalog(String defaultCatalog);
/**
* If non null, during connection creation the method {@link java.sql.Connection#setReadOnly(boolean)} will be called with the set value.
* @return null if not set and accepting the driver default otherwise the read only value
*/
public Boolean isDefaultReadOnly();
/**
* If non null, during connection creation the method {@link java.sql.Connection#setReadOnly(boolean)} will be called with the set value.
* @return null if not set and accepting the driver default otherwise the read only value
*/
public Boolean getDefaultReadOnly();
/**
* If non null, during connection creation the method {@link java.sql.Connection#setReadOnly(boolean)} will be called with the set value.
* @param defaultReadOnly null if not set and accepting the driver default.
*/
public void setDefaultReadOnly(Boolean defaultReadOnly);
/**
* Returns the default transaction isolation level. If set to {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION} the method
* {@link java.sql.Connection#setTransactionIsolation(int)} will not be called during connection creation.
* @return driver transaction isolation level, or -1 {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION} if not set.
*/
public int getDefaultTransactionIsolation();
/**
* If set to {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION} the method
* {@link java.sql.Connection#setTransactionIsolation(int)} will not be called during connection creation. Otherwise the method
* will be called with the isolation level set by this property.
* @param defaultTransactionIsolation a value of {@link java.sql.Connection#TRANSACTION_NONE}, {@link java.sql.Connection#TRANSACTION_READ_COMMITTED},
* {@link java.sql.Connection#TRANSACTION_READ_UNCOMMITTED}, {@link java.sql.Connection#TRANSACTION_REPEATABLE_READ},
* {@link java.sql.Connection#TRANSACTION_SERIALIZABLE} or {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION}
* The last value will not be set on the connection.
*/
public void setDefaultTransactionIsolation(int defaultTransactionIsolation);
/**
* The fully qualified Java class name of the JDBC driver to be used. The driver has to be accessible from the same classloader as tomcat-jdbc.jar
* @return fully qualified JDBC driver name.
*/
public String getDriverClassName();
/**
* The fully qualified Java class name of the JDBC driver to be used. The driver has to be accessible from the same classloader as tomcat-jdbc.jar
* @param driverClassName a fully qualified Java class name of a {@link java.sql.Driver} implementation.
*/
public void setDriverClassName(String driverClassName);
/**
* Returns the number of connections that will be established when the connection pool is started.
* Default value is 10
* @return number of connections to be started when pool is started
*/
public int getInitialSize();
/**
* Set the number of connections that will be established when the connection pool is started.
* Default value is 10.
* If this value exceeds {@link #setMaxActive(int)} it will automatically be lowered.
* @param initialSize the number of connections to be established.
*
*/
public void setInitialSize(int initialSize);
/**
* boolean flag to set if stack traces should be logged for application code which abandoned a Connection.
* Logging of abandoned Connections adds overhead for every Connection borrow because a stack trace has to be generated.
* The default value is false.
* @return true if the connection pool logs stack traces when connections are borrowed from the pool.
*/
public boolean isLogAbandoned();
/**
* boolean flag to set if stack traces should be logged for application code which abandoned a Connection.
* Logging of abandoned Connections adds overhead for every Connection borrow because a stack trace has to be generated.
* The default value is false.
* @param logAbandoned set to true if stack traces should be recorded when {@link DataSource#getConnection()} is called.
*/
public void setLogAbandoned(boolean logAbandoned);
/**
* The maximum number of active connections that can be allocated from this pool at the same time. The default value is 100
* @return the maximum number of connections used by this pool
*/
public int getMaxActive();
/**
* The maximum number of active connections that can be allocated from this pool at the same time. The default value is 100
* @param maxActive hard limit for number of managed connections by this pool
*/
public void setMaxActive(int maxActive);
/**
* The maximum number of connections that should be kept in the idle pool if {@link #isPoolSweeperEnabled()} returns false.
* If the If {@link #isPoolSweeperEnabled()} returns true, then the idle pool can grow up to {@link #getMaxActive}
* and will be shrunk according to {@link #getMinEvictableIdleTimeMillis()} setting.
* Default value is maxActive:100
* @return the maximum number of idle connections.
*/
public int getMaxIdle();
/**
* The maximum number of connections that should be kept in the idle pool if {@link #isPoolSweeperEnabled()} returns false.
* If the If {@link #isPoolSweeperEnabled()} returns true, then the idle pool can grow up to {@link #getMaxActive}
* and will be shrunk according to {@link #getMinEvictableIdleTimeMillis()} setting.
* Default value is maxActive:100
* @param maxIdle the maximum size of the idle pool
*/
public void setMaxIdle(int maxIdle);
/**
* The maximum number of milliseconds that the pool will wait (when there are no available connections and the
* {@link #getMaxActive} has been reached) for a connection to be returned
* before throwing an exception. Default value is 30000 (30 seconds)
* @return the number of milliseconds to wait for a connection to become available if the pool is maxed out.
*/
public int getMaxWait();
/**
* The maximum number of milliseconds that the pool will wait (when there are no available connections and the
* {@link #getMaxActive} has been reached) for a connection to be returned
* before throwing an exception. Default value is 30000 (30 seconds)
* @param maxWait the maximum number of milliseconds to wait.
*/
public void setMaxWait(int maxWait);
/**
* The minimum amount of time an object must sit idle in the pool before it is eligible for eviction.
* The default value is 60000 (60 seconds).
* @return the minimum amount of idle time in milliseconds before a connection is considered idle and eligible for eviction.
*/
public int getMinEvictableIdleTimeMillis();
/**
* The minimum amount of time an object must sit idle in the pool before it is eligible for eviction.
* The default value is 60000 (60 seconds).
* @param minEvictableIdleTimeMillis the number of milliseconds a connection must be idle to be eligible for eviction.
*/
public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis);
/**
* The minimum number of established connections that should be kept in the pool at all times.
* The connection pool can shrink below this number if validation queries fail and connections get closed.
* Default value is derived from {@link #getInitialSize()} (also see {@link #setTestWhileIdle(boolean)}
* The idle pool will not shrink below this value during an eviction run, hence the number of actual connections
* can be between {@link #getMinIdle()} and somewhere between {@link #getMaxIdle()} and {@link #getMaxActive()}
* @return the minimum number of idle or established connections
*/
public int getMinIdle();
/**
* The minimum number of established connections that should be kept in the pool at all times.
* The connection pool can shrink below this number if validation queries fail and connections get closed.
* Default value is derived from {@link #getInitialSize()} (also see {@link #setTestWhileIdle(boolean)}
* The idle pool will not shrink below this value during an eviction run, hence the number of actual connections
* can be between {@link #getMinIdle()} and somewhere between {@link #getMaxIdle()} and {@link #getMaxActive()}
*
* @param minIdle the minimum number of idle or established connections
*/
public void setMinIdle(int minIdle);
/**
* Returns the name of the connection pool. By default a JVM unique random name is assigned.
* @return the name of the pool, should be unique in a JVM
*/
public String getName();
/**
* Sets the name of the connection pool
* @param name the name of the pool, should be unique in a runtime JVM
*/
public void setName(String name);
/**
* Property not used
* @return unknown value
*/
public int getNumTestsPerEvictionRun();
/**
* Property not used
* @param numTestsPerEvictionRun parameter ignored.
*/
public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun);
/**
* Returns the password used when establishing connections to the database.
* @return the password in string format
*/
public String getPassword();
/**
* Sets the password to establish the connection with.
* The password will be included as a database property with the name 'password'.
* @param password The password
* @see #getDbProperties()
*/
public void setPassword(String password);
/**
* @see #getName()
* @return the pool name
*/
public String getPoolName();
/**
* Returns the username used to establish the connection with
* @return the username used to establish the connection with
*/
public String getUsername();
/**
* Sets the username used to establish the connection with
* It will also be a property called 'user' in the database properties.
* @param username The user name
* @see #getDbProperties()
*/
public void setUsername(String username);
/**
* boolean flag to remove abandoned connections if they exceed the removeAbandonedTimout.
* If set to true a connection is considered abandoned and eligible for removal if it has
* been in use longer than the {@link #getRemoveAbandonedTimeout()} and the condition for
* {@link #getAbandonWhenPercentageFull()} is met.
* Setting this to true can recover db connections from applications that fail to close a connection.
* See also {@link #isLogAbandoned()} The default value is false.
* @return true if abandoned connections can be closed and expelled out of the pool
*/
public boolean isRemoveAbandoned();
/**
* boolean flag to remove abandoned connections if they exceed the removeAbandonedTimout.
* If set to true a connection is considered abandoned and eligible for removal if it has
* been in use longer than the {@link #getRemoveAbandonedTimeout()} and the condition for
* {@link #getAbandonWhenPercentageFull()} is met.
* Setting this to true can recover db connections from applications that fail to close a connection.
* See also {@link #isLogAbandoned()} The default value is false.
* @param removeAbandoned set to true if abandoned connections can be closed and expelled out of the pool
*/
public void setRemoveAbandoned(boolean removeAbandoned);
/**
* The time in seconds before a connection can be considered abandoned.
* The timer can be reset upon queries using an interceptor.
* @param removeAbandonedTimeout the time in seconds before a used connection can be considered abandoned
* @see org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer
*/
public void setRemoveAbandonedTimeout(int removeAbandonedTimeout);
/**
* The time in seconds before a connection can be considered abandoned.
* The timer can be reset upon queries using an interceptor.
* @see org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer
* @return the time in seconds before a used connection can be considered abandoned
*/
public int getRemoveAbandonedTimeout();
/**
* The indication of whether objects will be validated before being borrowed from the pool.
* If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another.
* NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string.
* Default value is false
* In order to have a more efficient validation, see {@link #setValidationInterval(long)}
* @return true if the connection is to be validated upon borrowing a connection from the pool
* @see #getValidationInterval()
*/
public boolean isTestOnBorrow();
/**
* The indication of whether objects will be validated before being borrowed from the pool.
* If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another.
* NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string.
* Default value is false
* In order to have a more efficient validation, see {@link #setValidationInterval(long)}
* @param testOnBorrow set to true if validation should take place before a connection is handed out to the application
* @see #getValidationInterval()
*/
public void setTestOnBorrow(boolean testOnBorrow);
/**
* The indication of whether objects will be validated after being returned to the pool.
* If the object fails to validate, it will be dropped from the pool.
* NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string.
* Default value is false
* In order to have a more efficient validation, see {@link #setValidationInterval(long)}
* @return true if validation should take place after a connection is returned to the pool
* @see #getValidationInterval()
*/
public boolean isTestOnReturn();
/**
* The indication of whether objects will be validated after being returned to the pool.
* If the object fails to validate, it will be dropped from the pool.
* NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string.
* Default value is false
* In order to have a more efficient validation, see {@link #setValidationInterval(long)}
* @param testOnReturn true if validation should take place after a connection is returned to the pool
* @see #getValidationInterval()
*/
public void setTestOnReturn(boolean testOnReturn);
/**
* Set to true if query validation should take place while the connection is idle.
* @return true if validation should take place during idle checks
* @see #setTimeBetweenEvictionRunsMillis(int)
*/
public boolean isTestWhileIdle();
/**
* Set to true if query validation should take place while the connection is idle.
* @param testWhileIdle true if validation should take place during idle checks
* @see #setTimeBetweenEvictionRunsMillis(int)
*/
public void setTestWhileIdle(boolean testWhileIdle);
/**
* The number of milliseconds to sleep between runs of the idle connection validation, abandoned cleaner
* and idle pool resizing. This value should not be set under 1 second.
* It dictates how often we check for idle, abandoned connections, and how often we validate idle connection and resize the idle pool.
* The default value is 5000 (5 seconds)
* @return the sleep time in between validations in milliseconds
*/
public int getTimeBetweenEvictionRunsMillis();
/**
* The number of milliseconds to sleep between runs of the idle connection validation, abandoned cleaner
* and idle pool resizing. This value should not be set under 1 second.
* It dictates how often we check for idle, abandoned connections, and how often we validate idle connection and resize the idle pool.
* The default value is 5000 (5 seconds)
* @param timeBetweenEvictionRunsMillis the sleep time in between validations in milliseconds
*/
public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis);
/**
* The URL used to connect to the database
* @return the configured URL for this connection pool
* @see java.sql.Driver#connect(String, Properties)
*/
public String getUrl();
/**
* Sets the URL used to connect to the database
* @param url the configured URL for this connection pool
* @see java.sql.Driver#connect(String, Properties)
*/
public void setUrl(String url);
/**
* The SQL query that will be used to validate connections from this
* pool before returning them to the caller or pool.
* If specified, this query does not have to return any data,
* it just can't throw an SQLException.
* The default value is null.
* Example values are SELECT 1(mysql),
* select 1 from dual(oracle),
* SELECT 1(MS Sql Server)
* @return the query used for validation or null if no validation is performed
*/
public String getValidationQuery();
/**
* The SQL query that will be used to validate connections from this
* pool before returning them to the caller or pool.
* If specified, this query does not have to return any data,
* it just can't throw an SQLException.
* The default value is null.
* Example values are SELECT 1(mysql),
* select 1 from dual(oracle),
* SELECT 1(MS Sql Server)
* @param validationQuery the query used for validation or null if no validation is performed
*/
public void setValidationQuery(String validationQuery);
/**
* The timeout in seconds before a connection validation queries fail.
* A value less than or equal to zero will disable this feature. Defaults to -1.
* @return the timeout value in seconds
*/
public int getValidationQueryTimeout();
/**
* The timeout in seconds before a connection validation queries fail.
* A value less than or equal to zero will disable this feature. Defaults to -1.
* @param validationQueryTimeout The timeout value
*/
public void setValidationQueryTimeout(int validationQueryTimeout);
/**
* Return the name of the optional validator class - may be null.
*
* @return the name of the optional validator class - may be null
*/
public String getValidatorClassName();
/**
* Set the name for an optional validator class which will be used in place of test queries. If set to
* null, standard validation will be used.
*
* @param className the name of the optional validator class
*/
public void setValidatorClassName(String className);
/**
* @return the optional validator object - may be null
*/
public Validator getValidator();
/**
* Sets the validator object
* If this is a non null object, it will be used as a validator instead of the validationQuery
* If this is null, remove the usage of the validator.
* @param validator The validator object
*/
public void setValidator(Validator validator);
/**
* avoid excess validation, only run validation at most at this frequency - time in milliseconds.
* If a connection is due for validation, but has been validated previously
* within this interval, it will not be validated again.
* The default value is 3000 (3 seconds).
* @return the validation interval in milliseconds
*/
public long getValidationInterval();
/**
* avoid excess validation, only run validation at most at this frequency - time in milliseconds.
* If a connection is due for validation, but has been validated previously
* within this interval, it will not be validated again.
* The default value is 3000 (3 seconds).
* @param validationInterval the validation interval in milliseconds
*/
public void setValidationInterval(long validationInterval);
/**
* A custom query to be run when a connection is first created. The default value is null.
* This query only runs once per connection, and that is when a new connection is established to the database.
* If this value is non null, it will replace the validation query during connection creation.
* @return the init SQL used to run against the DB or null if not set
*/
public String getInitSQL();
/**
* A custom query to be run when a connection is first created. The default value is null.
* This query only runs once per connection, and that is when a new connection is established to the database.
* If this value is non null, it will replace the validation query during connection creation.
* @param initSQL the init SQL used to run against the DB or null if no query should be executed
*/
public void setInitSQL(String initSQL);
/**
* Returns true if we should run the validation query when connecting to the database for the first time on a connection.
* Normally this is always set to false, unless one wants to use the validationQuery as an init query.
* @return true if we should run the validation query upon connect
*/
public boolean isTestOnConnect();
/**
* Set to true if we should run the validation query when connecting to the database for the first time on a connection.
* Normally this is always set to false, unless one wants to use the validationQuery as an init query.
* Setting an {@link #setInitSQL(String)} will override this setting, as the init SQL will be used instead of the validation query
* @param testOnConnect set to true if we should run the validation query upon connect
*/
public void setTestOnConnect(boolean testOnConnect);
/**
* A semicolon separated list of classnames extending {@link org.apache.tomcat.jdbc.pool.JdbcInterceptor} class.
* These interceptors will be inserted as an interceptor into the chain of operations on a java.sql.Connection object.
* Example interceptors are {@link org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer StatementFinalizer} to close all
* used statements during the session.
* {@link org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer ResetAbandonedTimer} resets the timer upon every operation
* on the connection or a statement.
* {@link org.apache.tomcat.jdbc.pool.interceptor.ConnectionState ConnectionState} caches the auto commit, read only and catalog settings to avoid round trips to the DB.
* The default value is null.
* @return the interceptors that are used for connections.
* Example format: 'ConnectionState(useEquals=true,fast=yes);ResetAbandonedTimer'
*/
public String getJdbcInterceptors();
/**
* A semicolon separated list of classnames extending {@link org.apache.tomcat.jdbc.pool.JdbcInterceptor} class.
* These interceptors will be inserted as an interceptor into the chain of operations on a java.sql.Connection object.
* Example interceptors are {@link org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer StatementFinalizer} to close all
* used statements during the session.
* {@link org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer ResetAbandonedTimer} resets the timer upon every operation
* on the connection or a statement.
* {@link org.apache.tomcat.jdbc.pool.interceptor.ConnectionState ConnectionState} caches the auto commit, read only and catalog settings to avoid round trips to the DB.
* The default value is null.
* @param jdbcInterceptors the interceptors that are used for connections.
* Example format: 'ConnectionState(useEquals=true,fast=yes);ResetAbandonedTimer'
*/
public void setJdbcInterceptors(String jdbcInterceptors);
/**
* Returns the {@link #getJdbcInterceptors()} as an array of objects with properties and the classes.
* @return an array of interceptors that have been configured
*/
public InterceptorDefinition[] getJdbcInterceptorsAsArray();
/**
* If set to true, the connection pool creates a {@link org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean} object
* that can be registered with JMX to receive notifications and state about the pool.
* The ConnectionPool object doesn't register itself, as there is no way to keep a static non changing ObjectName across JVM restarts.
* @return true if the mbean object will be created upon startup.
*/
public boolean isJmxEnabled();
/**
* If set to true, the connection pool creates a {@link org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean} object
* that can be registered with JMX to receive notifications and state about the pool.
* The ConnectionPool object doesn't register itself, as there is no way to keep a static non changing ObjectName across JVM restarts.
* @param jmxEnabled set to to if the mbean object should be created upon startup.
*/
public void setJmxEnabled(boolean jmxEnabled);
/**
* Returns true if the pool sweeper is enabled for the connection pool.
* The pool sweeper is enabled if any settings that require async intervention in the pool are turned on
* <code>
boolean result = getTimeBetweenEvictionRunsMillis()&gt;0;
result = result &amp;&amp; (isRemoveAbandoned() &amp;&amp; getRemoveAbandonedTimeout()&gt;0);
result = result || (isTestWhileIdle() &amp;&amp; getValidationQuery()!=null);
return result;
</code>
*
* @return true if a background thread is or will be enabled for this pool
*/
public boolean isPoolSweeperEnabled();
/**
* Set to true if you wish the <code>ProxyConnection</code> class to use <code>String.equals</code> instead of
* <code>==</code> when comparing method names.
* This property does not apply to added interceptors as those are configured individually.
* The default value is <code>false</code>.
* @return true if pool uses {@link String#equals(Object)} instead of == when comparing method names on {@link java.sql.Connection} methods
*/
public boolean isUseEquals();
/**
* Set to true if you wish the <code>ProxyConnection</code> class to use <code>String.equals</code> instead of
* <code>==</code> when comparing method names.
* This property does not apply to added interceptors as those are configured individually.
* The default value is <code>false</code>.
* @param useEquals set to true if the pool should use {@link String#equals(Object)} instead of ==
* when comparing method names on {@link java.sql.Connection} methods
*/
public void setUseEquals(boolean useEquals);
/**
* Time in milliseconds to keep this connection before reconnecting.
* When a connection is idle, returned to the pool or borrowed from the pool, the pool will
* check to see if the ((now - time-when-connected) &gt; maxAge) has been reached, and if so,
* it reconnects. Note that the age of idle connections will only be checked if
* {@link #getTimeBetweenEvictionRunsMillis()} returns a value greater than 0.
* The default value is 0, which implies that connections will be left open and no
* age checks will be done.
* This is a useful setting for database sessions that leak memory as it ensures that the session
* will have a finite life span.
* @return the time in milliseconds a connection will be open for when used
*/
public long getMaxAge();
/**
* Time in milliseconds to keep this connection before reconnecting.
* When a connection is idle, returned to the pool or borrowed from the pool, the pool will
* check to see if the ((now - time-when-connected) &gt; maxAge) has been reached, and if so,
* it reconnects. Note that the age of idle connections will only be checked if
* {@link #getTimeBetweenEvictionRunsMillis()} returns a value greater than 0.
* The default value is 0, which implies that connections will be left open and no
* age checks will be done.
* This is a useful setting for database sessions that leak memory as it ensures that the session
* will have a finite life span.
* @param maxAge the time in milliseconds a connection will be open for when used
*/
public void setMaxAge(long maxAge);
/**
* Return true if a lock should be used when operations are performed on the connection object.
* Should be set to false unless you plan to have a background thread of your own doing idle and abandon checking
* such as JMX clients. If the pool sweeper is enabled, then the lock will automatically be used regardless of this setting.
* @return true if a lock is used.
*/
public boolean getUseLock();
/**
* Set to true if a lock should be used when operations are performed on the connection object.
* Should be set to false unless you plan to have a background thread of your own doing idle and abandon checking
* such as JMX clients. If the pool sweeper is enabled, then the lock will automatically be used regardless of this setting.
* @param useLock set to true if a lock should be used on connection operations
*/
public void setUseLock(boolean useLock);
/**
* Similar to {@link #setRemoveAbandonedTimeout(int)} but instead of treating the connection
* as abandoned, and potentially closing the connection, this simply logs the warning if
* {@link #isLogAbandoned()} returns true. If this value is equal or less than 0, no suspect
* checking will be performed. Suspect checking only takes place if the timeout value is larger than 0 and
* the connection was not abandoned or if abandon check is disabled. If a connection is suspect a WARN message gets
* logged and a JMX notification gets sent once.
* @param seconds - the amount of time in seconds that has to pass before a connection is marked suspect.
*/
public void setSuspectTimeout(int seconds);
/**
* Returns the time in seconds to pass before a connection is marked an abandoned suspect.
* Any value lesser than or equal to 0 means the check is disabled.
* @return Returns the time in seconds to pass before a connection is marked an abandoned suspect.
*/
public int getSuspectTimeout();
/**
* Injects a datasource that will be used to retrieve/create connections.
* If a data source is set, the {@link PoolConfiguration#getUrl()} and {@link PoolConfiguration#getDriverClassName()} methods are ignored
* and not used by the pool. If the {@link PoolConfiguration#getUsername()} and {@link PoolConfiguration#getPassword()}
* values are set, the method {@link javax.sql.DataSource#getConnection(String, String)} method will be called instead of the
* {@link javax.sql.DataSource#getConnection()} method.
* If the data source implements {@link javax.sql.XADataSource} the methods
* {@link javax.sql.XADataSource#getXAConnection()} and {@link javax.sql.XADataSource#getXAConnection(String,String)}
* will be invoked.
* @param ds the {@link javax.sql.DataSource} to be used for creating connections to be pooled.
*/
public void setDataSource(Object ds);
/**
* Returns a datasource, if one exists that is being used to create connections.
* This method will return null if the pool is using a {@link java.sql.Driver}
* @return the {@link javax.sql.DataSource} to be used for creating connections to be pooled or null if a Driver is used.
*/
public Object getDataSource();
/**
* Configure the connection pool to use a DataSource according to {@link PoolConfiguration#setDataSource(Object)}
* But instead of injecting the object, specify the JNDI location.
* After a successful JNDI look, the {@link PoolConfiguration#getDataSource()} will not return null.
* @param jndiDS -the JNDI string @TODO specify the rules here.
*/
public void setDataSourceJNDI(String jndiDS);
/**
* Returns the JNDI string configured for data source usage.
* @return the JNDI string or null if not set
*/
public String getDataSourceJNDI();
/**
* Returns true if the call {@link DataSource#getConnection(String, String) getConnection(username,password)} is
* allowed. This is used for when the pool is used by an application accessing multiple schemas.
* There is a performance impact turning this option on.
* @return true if {@link DataSource#getConnection(String, String) getConnection(username,password)} is honored, false if it is ignored.
*/
public boolean isAlternateUsernameAllowed();
/**
* Set to true if the call {@link DataSource#getConnection(String, String) getConnection(username,password)} is
* allowed and honored.. This is used for when the pool is used by an application accessing multiple schemas.
* There is a performance impact turning this option on, even when not used due to username checks.
* @param alternateUsernameAllowed - set true if {@link DataSource#getConnection(String, String) getConnection(username,password)} is honored,
* false if it is to be ignored.
*/
public void setAlternateUsernameAllowed(boolean alternateUsernameAllowed);
/**
* Set to true if you want the connection pool to commit any pending transaction when a connection is returned.
* The default value is false, as this could result in committing data.
* This parameter is only looked at if the {@link #getDefaultAutoCommit()} returns false
* @param commitOnReturn set to true if the pool should call {@link java.sql.Connection#commit()} when a connection is returned to the pool.
* Default is false
*/
public void setCommitOnReturn(boolean commitOnReturn);
/**
* @see PoolConfiguration#setCommitOnReturn(boolean)
* @return <code>true</code> if the pool should commit when a connection is returned to it
*/
public boolean getCommitOnReturn();
/**
* Set to true if you want the connection pool to rollback any pending transaction when a connection is returned.
* The default value is false, as this could result in committing data.
* This parameter is only looked at if the {@link #getDefaultAutoCommit()} returns false
* @param rollbackOnReturn set to true if the pool should call {@link java.sql.Connection#rollback()} when a connection is returned to the pool.
* Default is false
*/
public void setRollbackOnReturn(boolean rollbackOnReturn);
/**
* @see PoolConfiguration#setRollbackOnReturn(boolean)
* @return <code>true</code> if the pool should rollback when a connection is returned to it
*/
public boolean getRollbackOnReturn();
/**
* If set to <code>true</code>, the connection will be wrapped with facade that will disallow the connection to be used after
* {@link java.sql.Connection#close()} is called. If set to <code>true</code>, after {@link java.sql.Connection#close()} all calls except
* {@link java.sql.Connection#close()} and {@link java.sql.Connection#isClosed()} will throw an exception.
* @param useDisposableConnectionFacade <code>true</code> to use a facade
*/
public void setUseDisposableConnectionFacade(boolean useDisposableConnectionFacade);
/**
* Returns <code>true</code> if this connection pool is configured to use a connection facade to prevent re-use of connection after
* {@link java.sql.Connection#close()} has been invoked
* @return <code>true</code> if {@link java.sql.Connection#close()} has been invoked.
*/
public boolean getUseDisposableConnectionFacade();
/**
* Set to true if you wish that errors from validation should be logged as error messages.
* @param logValidationErrors set to true to log validation errors
*/
public void setLogValidationErrors(boolean logValidationErrors);
/**
* Returns true if errors that happen during validation will be logged
* @return true if errors that happen during validation will be logged
*/
public boolean getLogValidationErrors();
/**
* Returns true if the pool is configured to propagate interrupt state of a thread.
* A thread waiting for a connection, can have its wait interrupted, and by default
* will clear the interrupt flag and throw a {@link PoolExhaustedException}
* @return true if the pool is configured to propagate and not clear the thread interrupt state
*/
public boolean getPropagateInterruptState();
/**
* Configure the pool to propagate interrupt state for interrupted threads waiting for a connection
* A thread waiting for a connection, can have its wait interrupted, and by default
* will clear the interrupt flag and throw a {@link PoolExhaustedException}
* If set to true, this behavior will change, while the {@link PoolExhaustedException} is still thrown, the threads interrupted state is still set.
* @param propagateInterruptState - set to true to not clear, but propagate, a threads interrupted state.
*/
public void setPropagateInterruptState(boolean propagateInterruptState);
/**
* Set to true if you want to ignore error of connection creation while initializing the pool.
* Set to false if you want to fail the initialization of the pool by throwing exception.
* @param ignoreExceptionOnPreLoad set to true if you want to ignore error of connection creation while initializing the pool.
*/
public void setIgnoreExceptionOnPreLoad(boolean ignoreExceptionOnPreLoad);
/**
* @return <code>true</code> to ignore exceptions
* @see PoolConfiguration#setIgnoreExceptionOnPreLoad(boolean)
*/
public boolean isIgnoreExceptionOnPreLoad();
/**
* Set this to true if you wish to wrap statements in order to enable equals() and hashCode()
* methods to be called on the closed statements if any statement proxy is set.
* @param useStatementFacade set to <code>true</code> to wrap statements
*/
public void setUseStatementFacade(boolean useStatementFacade);
/**
* Returns <code>true</code> if this connection pool is configured to wrap statements in order
* to enable equals() and hashCode() methods to be called on the closed statements if any
* statement proxy is set.
* @return <code>true</code> if the statements are wrapped
*/
public boolean getUseStatementFacade();
}

View File

@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.sql.SQLException;
public class PoolExhaustedException extends SQLException {
private static final long serialVersionUID = 3501536931777262475L;
public PoolExhaustedException() {
}
public PoolExhaustedException(String reason) {
super(reason);
}
public PoolExhaustedException(Throwable cause) {
super(cause);
}
public PoolExhaustedException(String reason, String SQLState) {
super(reason, SQLState);
}
public PoolExhaustedException(String reason, Throwable cause) {
super(reason, cause);
}
public PoolExhaustedException(String reason, String SQLState, int vendorCode) {
super(reason, SQLState, vendorCode);
}
public PoolExhaustedException(String reason, String sqlState, Throwable cause) {
super(reason, sqlState, cause);
}
public PoolExhaustedException(String reason, String sqlState, int vendorCode, Throwable cause) {
super(reason, sqlState, vendorCode, cause);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.util.Properties;
public class PoolUtilities {
public static final String PROP_USER = "user";
public static final String PROP_PASSWORD = "password";
public static Properties clone(Properties p) {
Properties c = new Properties();
c.putAll(p);
return c;
}
public static Properties cloneWithoutPassword(Properties p) {
Properties result = clone(p);
result.remove(PROP_PASSWORD);
return result;
}
}

View File

@@ -0,0 +1,877 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
import org.apache.tomcat.jdbc.pool.jmx.JmxUtil;
/**
* Represents a pooled connection
* and holds a reference to the {@link java.sql.Connection} object
* @version 1.0
*/
public class PooledConnection implements PooledConnectionMBean {
/**
* Logger
*/
private static final Log log = LogFactory.getLog(PooledConnection.class);
public static final String PROP_USER = PoolUtilities.PROP_USER;
public static final String PROP_PASSWORD = PoolUtilities.PROP_PASSWORD;
/**
* Validate when connection is borrowed flag
*/
public static final int VALIDATE_BORROW = 1;
/**
* Validate when connection is returned flag
*/
public static final int VALIDATE_RETURN = 2;
/**
* Validate when connection is idle flag
*/
public static final int VALIDATE_IDLE = 3;
/**
* Validate when connection is initialized flag
*/
public static final int VALIDATE_INIT = 4;
/**
* The properties for the connection pool
*/
protected PoolConfiguration poolProperties;
/**
* The underlying database connection
*/
private volatile java.sql.Connection connection;
/**
* If using a XAConnection underneath.
*/
protected volatile javax.sql.XAConnection xaConnection;
/**
* When we track abandon traces, this string holds the thread dump
*/
private String abandonTrace = null;
/**
* Timestamp the connection was last 'touched' by the pool
*/
private volatile long timestamp;
/**
* Lock for this connection only
*/
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false);
/**
* Set to true if this connection has been discarded by the pool
*/
private volatile boolean discarded = false;
/**
* The Timestamp when the last time the connect() method was called successfully
*/
private volatile long lastConnected = -1;
/**
* timestamp to keep track of validation intervals
*/
private volatile long lastValidated = System.currentTimeMillis();
/**
* The parent
*/
protected ConnectionPool parent;
private HashMap<Object, Object> attributes = new HashMap<>();
private volatile long connectionVersion=0;
private static final AtomicLong connectionIndex = new AtomicLong(0);
private ObjectName oname = null;
/**
* Weak reference to cache the list of interceptors for this connection
* so that we don't create a new list of interceptors each time we borrow
* the connection
*/
private volatile JdbcInterceptor handler = null;
private AtomicBoolean released = new AtomicBoolean(false);
private volatile boolean suspect = false;
private java.sql.Driver driver = null;
/**
* Constructor
* @param prop - pool properties
* @param parent - the parent connection pool
*/
public PooledConnection(PoolConfiguration prop, ConnectionPool parent) {
poolProperties = prop;
this.parent = parent;
connectionVersion = parent.getPoolVersion();
}
@Override
public long getConnectionVersion() {
return connectionVersion;
}
/**
* @deprecated use {@link #shouldForceReconnect(String, String)}
* method kept since it was public, to avoid changing interface.
* @param username The user name
* @param password The password
* @return <code>true</code>if the pool does not need to reconnect
*/
@Deprecated
public boolean checkUser(String username, String password) {
return !shouldForceReconnect(username, password);
}
/**
* Returns true if we must force reconnect based on credentials passed in.
* Returns false if {@link PoolConfiguration#isAlternateUsernameAllowed()} method returns false.
* Returns false if the username/password has not changed since this connection was connected
* @param username the username you wish to connect with, pass in null to accept the default username from {@link PoolConfiguration#getUsername()}
* @param password the password you wish to connect with, pass in null to accept the default username from {@link org.apache.tomcat.jdbc.pool.PoolConfiguration#getPassword()}
* @return true is the pool must reconnect
*/
public boolean shouldForceReconnect(String username, String password) {
if (!getPoolProperties().isAlternateUsernameAllowed()) return false;
if (username==null) username = poolProperties.getUsername();
if (password==null) password = poolProperties.getPassword();
String storedUsr = (String)getAttributes().get(PROP_USER);
String storedPwd = (String)getAttributes().get(PROP_PASSWORD);
boolean noChangeInCredentials = (username==null && storedUsr==null);
noChangeInCredentials = (noChangeInCredentials || (username!=null && username.equals(storedUsr)));
noChangeInCredentials = noChangeInCredentials && ((password==null && storedPwd==null) || (password!=null && password.equals(storedPwd)));
if (username==null) getAttributes().remove(PROP_USER); else getAttributes().put(PROP_USER, username);
if (password==null) getAttributes().remove(PROP_PASSWORD); else getAttributes().put(PROP_PASSWORD, password);
return !noChangeInCredentials;
}
/**
* Connects the underlying connection to the database.
* @throws SQLException if the method {@link #release()} has been called.
* @throws SQLException if driver instantiation fails
* @throws SQLException if a call to {@link java.sql.Driver#connect(String, java.util.Properties)} fails.
* @throws SQLException if default properties are configured and a call to
* {@link java.sql.Connection#setAutoCommit(boolean)}, {@link java.sql.Connection#setCatalog(String)},
* {@link java.sql.Connection#setTransactionIsolation(int)} or {@link java.sql.Connection#setReadOnly(boolean)} fails.
*/
public void connect() throws SQLException {
if (released.get()) throw new SQLException("A connection once released, can't be reestablished.");
if (connection != null) {
try {
this.disconnect(false);
} catch (Exception x) {
log.debug("Unable to disconnect previous connection.", x);
} //catch
} //end if
//if (poolProperties.getDataSource()==null && poolProperties.getDataSourceJNDI()!=null) {
//TODO lookup JNDI name
//}
if (poolProperties.getDataSource()!=null) {
connectUsingDataSource();
} else {
connectUsingDriver();
}
//set up the default state, unless we expect the interceptor to do it
if (poolProperties.getJdbcInterceptors()==null || poolProperties.getJdbcInterceptors().indexOf(ConnectionState.class.getName())<0 ||
poolProperties.getJdbcInterceptors().indexOf(ConnectionState.class.getSimpleName())<0) {
if (poolProperties.getDefaultTransactionIsolation()!=DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION) connection.setTransactionIsolation(poolProperties.getDefaultTransactionIsolation());
if (poolProperties.getDefaultReadOnly()!=null) connection.setReadOnly(poolProperties.getDefaultReadOnly().booleanValue());
if (poolProperties.getDefaultAutoCommit()!=null) connection.setAutoCommit(poolProperties.getDefaultAutoCommit().booleanValue());
if (poolProperties.getDefaultCatalog()!=null) connection.setCatalog(poolProperties.getDefaultCatalog());
}
this.discarded = false;
this.lastConnected = System.currentTimeMillis();
}
protected void connectUsingDataSource() throws SQLException {
String usr = null;
String pwd = null;
if (getAttributes().containsKey(PROP_USER)) {
usr = (String) getAttributes().get(PROP_USER);
} else {
usr = poolProperties.getUsername();
getAttributes().put(PROP_USER, usr);
}
if (getAttributes().containsKey(PROP_PASSWORD)) {
pwd = (String) getAttributes().get(PROP_PASSWORD);
} else {
pwd = poolProperties.getPassword();
getAttributes().put(PROP_PASSWORD, pwd);
}
if (poolProperties.getDataSource() instanceof javax.sql.XADataSource) {
javax.sql.XADataSource xds = (javax.sql.XADataSource)poolProperties.getDataSource();
if (usr!=null && pwd!=null) {
xaConnection = xds.getXAConnection(usr, pwd);
connection = xaConnection.getConnection();
} else {
xaConnection = xds.getXAConnection();
connection = xaConnection.getConnection();
}
} else if (poolProperties.getDataSource() instanceof javax.sql.DataSource){
javax.sql.DataSource ds = (javax.sql.DataSource)poolProperties.getDataSource();
if (usr!=null && pwd!=null) {
connection = ds.getConnection(usr, pwd);
} else {
connection = ds.getConnection();
}
} else if (poolProperties.getDataSource() instanceof javax.sql.ConnectionPoolDataSource){
javax.sql.ConnectionPoolDataSource ds = (javax.sql.ConnectionPoolDataSource)poolProperties.getDataSource();
if (usr!=null && pwd!=null) {
connection = ds.getPooledConnection(usr, pwd).getConnection();
} else {
connection = ds.getPooledConnection().getConnection();
}
} else {
throw new SQLException("DataSource is of unknown class:"+(poolProperties.getDataSource()!=null?poolProperties.getDataSource().getClass():"null"));
}
}
protected void connectUsingDriver() throws SQLException {
try {
if (driver==null) {
if (log.isDebugEnabled()) {
log.debug("Instantiating driver using class: "+poolProperties.getDriverClassName()+" [url="+poolProperties.getUrl()+"]");
}
if (poolProperties.getDriverClassName()==null) {
//rely on DriverManager
log.warn("Not loading a JDBC driver as driverClassName property is null.");
} else {
driver = (java.sql.Driver)
ClassLoaderUtil.loadClass(
poolProperties.getDriverClassName(),
PooledConnection.class.getClassLoader(),
Thread.currentThread().getContextClassLoader()
).getConstructor().newInstance();
}
}
} catch (java.lang.Exception cn) {
if (log.isDebugEnabled()) {
log.debug("Unable to instantiate JDBC driver.", cn);
}
SQLException ex = new SQLException(cn.getMessage());
ex.initCause(cn);
throw ex;
}
String driverURL = poolProperties.getUrl();
String usr = null;
String pwd = null;
if (getAttributes().containsKey(PROP_USER)) {
usr = (String) getAttributes().get(PROP_USER);
} else {
usr = poolProperties.getUsername();
getAttributes().put(PROP_USER, usr);
}
if (getAttributes().containsKey(PROP_PASSWORD)) {
pwd = (String) getAttributes().get(PROP_PASSWORD);
} else {
pwd = poolProperties.getPassword();
getAttributes().put(PROP_PASSWORD, pwd);
}
Properties properties = PoolUtilities.clone(poolProperties.getDbProperties());
if (usr != null) properties.setProperty(PROP_USER, usr);
if (pwd != null) properties.setProperty(PROP_PASSWORD, pwd);
try {
if (driver==null) {
connection = DriverManager.getConnection(driverURL, properties);
} else {
connection = driver.connect(driverURL, properties);
}
} catch (Exception x) {
if (log.isDebugEnabled()) {
log.debug("Unable to connect to database.", x);
}
if (parent.jmxPool!=null) {
parent.jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_CONNECT,
ConnectionPool.getStackTrace(x));
}
if (x instanceof SQLException) {
throw (SQLException)x;
} else {
SQLException ex = new SQLException(x.getMessage());
ex.initCause(x);
throw ex;
}
}
if (connection==null) {
throw new SQLException("Driver:"+driver+" returned null for URL:"+driverURL);
}
}
/**
*
* @return true if connect() was called successfully and disconnect has not yet been called
*/
@Override
public boolean isInitialized() {
return connection!=null;
}
/**
* Returns true if the connection has been connected more than
* {@link PoolConfiguration#getMaxAge()} milliseconds. false otherwise.
* @return Returns true if the connection has been connected more than
* {@link PoolConfiguration#getMaxAge()} milliseconds. false otherwise.
*/
@Override
public boolean isMaxAgeExpired() {
if (getPoolProperties().getMaxAge()>0 ) {
return (System.currentTimeMillis() - getLastConnected()) > getPoolProperties().getMaxAge();
} else {
return false;
}
}
/**
* Issues a call to {@link #disconnect(boolean)} with the argument false followed by a call to
* {@link #connect()}
* @throws SQLException if the call to {@link #connect()} fails.
*/
public void reconnect() throws SQLException {
this.disconnect(false);
this.connect();
} //reconnect
/**
* Disconnects the connection. All exceptions are logged using debug level.
* @param finalize if set to true, a call to {@link ConnectionPool#finalize(PooledConnection)} is called.
*/
private void disconnect(boolean finalize) {
if (isDiscarded() && connection == null) {
return;
}
setDiscarded(true);
if (connection != null) {
try {
parent.disconnectEvent(this, finalize);
if (xaConnection == null) {
connection.close();
} else {
xaConnection.close();
}
}catch (Exception ignore) {
if (log.isDebugEnabled()) {
log.debug("Unable to close underlying SQL connection",ignore);
}
}
}
connection = null;
xaConnection = null;
lastConnected = -1;
if (finalize) parent.finalize(this);
}
//============================================================================
//
//============================================================================
/**
* Returns abandon timeout in milliseconds
* @return abandon timeout in milliseconds
*/
public long getAbandonTimeout() {
if (poolProperties.getRemoveAbandonedTimeout() <= 0) {
return Long.MAX_VALUE;
} else {
return poolProperties.getRemoveAbandonedTimeout() * 1000L;
} //end if
}
/**
* Returns <code>true</code> if the connection pool is configured
* to do validation for a certain action.
* @param action The validation action
*/
private boolean doValidate(int action) {
if (action == PooledConnection.VALIDATE_BORROW &&
poolProperties.isTestOnBorrow())
return true;
else if (action == PooledConnection.VALIDATE_RETURN &&
poolProperties.isTestOnReturn())
return true;
else if (action == PooledConnection.VALIDATE_IDLE &&
poolProperties.isTestWhileIdle())
return true;
else if (action == PooledConnection.VALIDATE_INIT &&
poolProperties.isTestOnConnect())
return true;
else if (action == PooledConnection.VALIDATE_INIT &&
poolProperties.getInitSQL()!=null)
return true;
else
return false;
}
/**
* Returns <code>true</code> if the object is still valid. if not
* the pool will call the getExpiredAction() and follow up with one
* of the four expired methods
* @param validateAction The value
* @return <code>true</code> if the connection is valid
*/
public boolean validate(int validateAction) {
return validate(validateAction,null);
}
/**
* Validates a connection.
* @param validateAction the action used. One of {@link #VALIDATE_BORROW}, {@link #VALIDATE_IDLE},
* {@link #VALIDATE_INIT} or {@link #VALIDATE_RETURN}
* @param sql the SQL to be used during validation. If the {@link PoolConfiguration#setInitSQL(String)} has been called with a non null
* value and the action is {@link #VALIDATE_INIT} the init SQL will be used for validation.
*
* @return true if the connection was validated successfully. It returns true even if validation was not performed, such as when
* {@link PoolConfiguration#setValidationInterval(long)} has been called with a positive value.
* <p>
* false if the validation failed. The caller should close the connection if false is returned since a session could have been left in
* an unknown state during initialization.
*/
public boolean validate(int validateAction,String sql) {
if (this.isDiscarded()) {
return false;
}
if (!doValidate(validateAction)) {
//no validation required, no init sql and props not set
return true;
}
//Don't bother validating if already have recently enough
long now = System.currentTimeMillis();
if (validateAction!=VALIDATE_INIT &&
poolProperties.getValidationInterval() > 0 &&
(now - this.lastValidated) <
poolProperties.getValidationInterval()) {
return true;
}
if (poolProperties.getValidator() != null) {
if (poolProperties.getValidator().validate(connection, validateAction)) {
this.lastValidated = now;
return true;
} else {
if (getPoolProperties().getLogValidationErrors()) {
log.error("Custom validation through "+poolProperties.getValidator()+" failed.");
}
return false;
}
}
String query = sql;
if (validateAction == VALIDATE_INIT && poolProperties.getInitSQL() != null) {
query = poolProperties.getInitSQL();
}
if (query == null) {
query = poolProperties.getValidationQuery();
}
if (query == null) {
boolean transactionCommitted = false;
int validationQueryTimeout = poolProperties.getValidationQueryTimeout();
if (validationQueryTimeout < 0) validationQueryTimeout = 0;
try {
if (connection.isValid(validationQueryTimeout)) {
this.lastValidated = now;
transactionCommitted = silentlyCommitTransactionIfNeeded();
return true;
} else {
if (getPoolProperties().getLogValidationErrors()) {
log.error("isValid() returned false.");
}
return false;
}
} catch (SQLException e) {
if (getPoolProperties().getLogValidationErrors()) {
log.error("isValid() failed.", e);
} else if (log.isDebugEnabled()) {
log.debug("isValid() failed.", e);
}
return false;
} finally {
if (!transactionCommitted) {
silentlyRollbackTransactionIfNeeded();
}
}
}
boolean transactionCommitted = false;
Statement stmt = null;
try {
stmt = connection.createStatement();
int validationQueryTimeout = poolProperties.getValidationQueryTimeout();
if (validationQueryTimeout > 0) {
stmt.setQueryTimeout(validationQueryTimeout);
}
stmt.execute(query);
stmt.close();
this.lastValidated = now;
transactionCommitted = silentlyCommitTransactionIfNeeded();
return true;
} catch (Exception ex) {
if (getPoolProperties().getLogValidationErrors()) {
log.error("SQL Validation error", ex);
} else if (log.isDebugEnabled()) {
log.debug("Unable to validate object:",ex);
}
if (stmt!=null)
try { stmt.close();} catch (Exception ignore2){/*NOOP*/}
} finally {
if (!transactionCommitted) {
silentlyRollbackTransactionIfNeeded();
}
}
return false;
} //validate
private boolean silentlyCommitTransactionIfNeeded() {
try {
if (!connection.getAutoCommit()) {
connection.commit();
}
return true;
} catch (SQLException e) {
log.debug("Failed to commit transaction", e);
}
return false;
}
private boolean silentlyRollbackTransactionIfNeeded() {
try {
if (!connection.getAutoCommit()) {
connection.rollback();
}
return true;
} catch (SQLException e) {
log.debug("Failed to rollback transaction", e);
}
return false;
}
/**
* The time limit for how long the object
* can remain unused before it is released
* @return {@link PoolConfiguration#getMinEvictableIdleTimeMillis()}
*/
public long getReleaseTime() {
return this.poolProperties.getMinEvictableIdleTimeMillis();
}
/**
* This method is called if (Now - timeCheckedIn &gt; getReleaseTime())
* This method disconnects the connection, logs an error in debug mode if it happens
* then sets the {@link #released} flag to false. Any attempts to connect this cached object again
* will fail per {@link #connect()}
* The connection pool uses the atomic return value to decrement the pool size counter.
* @return true if this is the first time this method has been called. false if this method has been called before.
*/
public boolean release() {
try {
disconnect(true);
} catch (Exception x) {
if (log.isDebugEnabled()) {
log.debug("Unable to close SQL connection",x);
}
}
if (oname != null) {
JmxUtil.unregisterJmx(oname);
oname = null;
}
return released.compareAndSet(false, true);
}
/**
* The pool will set the stack trace when it is check out and
* checked in
* @param trace the stack trace for this connection
*/
public void setStackTrace(String trace) {
abandonTrace = trace;
}
/**
* Returns the stack trace from when this connection was borrowed. Can return null if no stack trace was set.
* @return the stack trace or null of no trace was set
*/
public String getStackTrace() {
return abandonTrace;
}
/**
* Sets a timestamp on this connection. A timestamp usually means that some operation
* performed successfully.
* @param timestamp the timestamp as defined by {@link System#currentTimeMillis()}
*/
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
setSuspect(false);
}
@Override
public boolean isSuspect() {
return suspect;
}
public void setSuspect(boolean suspect) {
this.suspect = suspect;
}
/**
* An interceptor can call this method with the value true, and the connection will be closed when it is returned to the pool.
* @param discarded - only valid value is true
* @throws IllegalStateException if this method is called with the value false and the value true has already been set.
*/
public void setDiscarded(boolean discarded) {
if (this.discarded && !discarded) throw new IllegalStateException("Unable to change the state once the connection has been discarded");
this.discarded = discarded;
}
/**
* Set the timestamp the connection was last validated.
* This flag is used to keep track when we are using a {@link PoolConfiguration#setValidationInterval(long) validation-interval}.
* @param lastValidated a timestamp as defined by {@link System#currentTimeMillis()}
*/
public void setLastValidated(long lastValidated) {
this.lastValidated = lastValidated;
}
/**
* Sets the pool configuration for this connection and connection pool.
* Object is shared with the {@link ConnectionPool}
* @param poolProperties The pool properties
*/
public void setPoolProperties(PoolConfiguration poolProperties) {
this.poolProperties = poolProperties;
}
/**
* Return the timestamps of last pool action. Timestamps are typically set when connections
* are borrowed from the pool. It is used to keep track of {@link PoolConfiguration#setRemoveAbandonedTimeout(int) abandon-timeouts}.
* This timestamp can also be reset by the {@link org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer#invoke(Object, java.lang.reflect.Method, Object[])}
* @return the timestamp of the last pool action as defined by {@link System#currentTimeMillis()}
*/
@Override
public long getTimestamp() {
return timestamp;
}
/**
* Returns the discarded flag.
* @return the discarded flag. If the value is true,
* either {@link #disconnect(boolean)} has been called or it will be called when the connection is returned to the pool.
*/
@Override
public boolean isDiscarded() {
return discarded;
}
/**
* Returns the timestamp of the last successful validation query execution.
* @return the timestamp of the last successful validation query execution as defined by {@link System#currentTimeMillis()}
*/
@Override
public long getLastValidated() {
return lastValidated;
}
/**
* Returns the configuration for this connection and pool
* @return the configuration for this connection and pool
*/
public PoolConfiguration getPoolProperties() {
return poolProperties;
}
/**
* Locks the connection only if either {@link PoolConfiguration#isPoolSweeperEnabled()} or
* {@link PoolConfiguration#getUseLock()} return true. The per connection lock ensures thread safety is
* multiple threads are performing operations on the connection.
* Otherwise this is a noop for performance
*/
public void lock() {
if (poolProperties.getUseLock() || this.poolProperties.isPoolSweeperEnabled()) {
//optimized, only use a lock when there is concurrency
lock.writeLock().lock();
}
}
/**
* Unlocks the connection only if the sweeper is enabled
* Otherwise this is a noop for performance
*/
public void unlock() {
if (poolProperties.getUseLock() || this.poolProperties.isPoolSweeperEnabled()) {
//optimized, only use a lock when there is concurrency
lock.writeLock().unlock();
}
}
/**
* Returns the underlying connection
* @return the underlying JDBC connection as it was returned from the JDBC driver
* @see javax.sql.PooledConnection#getConnection()
*/
public java.sql.Connection getConnection() {
return this.connection;
}
/**
* Returns the underlying XA connection
* @return the underlying XA connection as it was returned from the Datasource
*/
public javax.sql.XAConnection getXAConnection() {
return this.xaConnection;
}
/**
* Returns the timestamp of when the connection was last connected to the database.
* ie, a successful call to {@link java.sql.Driver#connect(String, java.util.Properties)}.
* @return the timestamp when this connection was created as defined by {@link System#currentTimeMillis()}
*/
@Override
public long getLastConnected() {
return lastConnected;
}
/**
* Returns the first handler in the interceptor chain
* @return the first interceptor for this connection
*/
public JdbcInterceptor getHandler() {
return handler;
}
public void setHandler(JdbcInterceptor handler) {
if (this.handler!=null && this.handler!=handler) {
JdbcInterceptor interceptor = this.handler;
while (interceptor!=null) {
interceptor.reset(null, null);
interceptor = interceptor.getNext();
}//while
}//end if
this.handler = handler;
}
@Override
public String toString() {
return "PooledConnection["+(connection!=null?connection.toString():"null")+"]";
}
/**
* Returns true if this connection has been released and wont be reused.
* @return true if the method {@link #release()} has been called
*/
@Override
public boolean isReleased() {
return released.get();
}
public HashMap<Object,Object> getAttributes() {
return attributes;
}
public void createMBean() {
if (oname != null) return;
String keyprop = ",connections=PooledConnection["+connectionIndex.getAndIncrement()+"]";
oname = JmxUtil.registerJmx(parent.getJmxPool().getObjectName(), keyprop, this);
}
public ObjectName getObjectName() {
return oname;
}
@Override
public void clearWarnings() {
try {
connection.clearWarnings();
} catch (SQLException e) {
log.warn("Unable to clear Warnings, connection will be closed.", e);
}
}
@Override
public boolean isClosed() throws SQLException {
return connection.isClosed();
}
@Override
public boolean getAutoCommit() throws SQLException {
return connection.getAutoCommit();
}
@Override
public String getCatalog() throws SQLException {
return connection.getCatalog();
}
@Override
public int getHoldability() throws SQLException {
return connection.getHoldability();
}
@Override
public boolean isReadOnly() throws SQLException {
return connection.isReadOnly();
}
@Override
public String getSchema() throws SQLException {
return connection.getSchema();
}
@Override
public int getTransactionIsolation() throws SQLException {
return connection.getTransactionIsolation();
}
}

View File

@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.sql.SQLException;
public interface PooledConnectionMBean {
// PooledConnection
public long getConnectionVersion();
public boolean isInitialized();
public boolean isMaxAgeExpired();
public boolean isSuspect();
public long getTimestamp();
public boolean isDiscarded();
public long getLastValidated();
public long getLastConnected();
public boolean isReleased();
// java.sql.Connection
public void clearWarnings();
public boolean isClosed() throws SQLException;
public boolean getAutoCommit() throws SQLException;
public String getCatalog() throws SQLException;
public int getHoldability() throws SQLException;
public boolean isReadOnly() throws SQLException;
public String getSchema() throws SQLException;
public int getTransactionIsolation() throws SQLException;
}

View File

@@ -0,0 +1,156 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import javax.sql.XAConnection;
/**
* A ProxyConnection object is the bottom most interceptor that wraps an object of type
* {@link PooledConnection}. The ProxyConnection intercepts three methods:
* <ul>
* <li>{@link java.sql.Connection#close()} - returns the connection to the pool. May be called multiple times.</li>
* <li>{@link java.lang.Object#toString()} - returns a custom string for this object</li>
* <li>{@link javax.sql.PooledConnection#getConnection()} - returns the underlying connection</li>
* </ul>
* By default method comparisons is done on a String reference level, unless the {@link PoolConfiguration#setUseEquals(boolean)} has been called
* with a <code>true</code> argument.
*/
public class ProxyConnection extends JdbcInterceptor {
protected PooledConnection connection = null;
protected ConnectionPool pool = null;
public PooledConnection getConnection() {
return connection;
}
public void setConnection(PooledConnection connection) {
this.connection = connection;
}
public ConnectionPool getPool() {
return pool;
}
public void setPool(ConnectionPool pool) {
this.pool = pool;
}
protected ProxyConnection(ConnectionPool parent, PooledConnection con,
boolean useEquals) {
pool = parent;
connection = con;
setUseEquals(useEquals);
}
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
this.pool = parent;
this.connection = con;
}
public boolean isWrapperFor(Class<?> iface) {
if (iface == XAConnection.class && connection.getXAConnection()!=null) {
return true;
} else {
return (iface.isInstance(connection.getConnection()));
}
}
public Object unwrap(Class<?> iface) throws SQLException {
if (iface == PooledConnection.class) {
return connection;
}else if (iface == XAConnection.class) {
return connection.getXAConnection();
} else if (isWrapperFor(iface)) {
return connection.getConnection();
} else {
throw new SQLException("Not a wrapper of "+iface.getName());
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (compare(ISCLOSED_VAL,method)) {
return Boolean.valueOf(isClosed());
}
if (compare(CLOSE_VAL,method)) {
if (connection==null) return null; //noop for already closed.
PooledConnection poolc = this.connection;
this.connection = null;
pool.returnConnection(poolc);
return null;
} else if (compare(TOSTRING_VAL,method)) {
return this.toString();
} else if (compare(GETCONNECTION_VAL,method) && connection!=null) {
return connection.getConnection();
} else if (method.getDeclaringClass().isAssignableFrom(XAConnection.class)) {
try {
return method.invoke(connection.getXAConnection(),args);
}catch (Throwable t) {
if (t instanceof InvocationTargetException) {
throw t.getCause() != null ? t.getCause() : t;
} else {
throw t;
}
}
}
if (isClosed()) throw new SQLException("Connection has already been closed.");
if (compare(UNWRAP_VAL,method)) {
return unwrap((Class<?>)args[0]);
} else if (compare(ISWRAPPERFOR_VAL,method)) {
return Boolean.valueOf(this.isWrapperFor((Class<?>)args[0]));
}
try {
PooledConnection poolc = connection;
if (poolc!=null) {
return method.invoke(poolc.getConnection(),args);
} else {
throw new SQLException("Connection has already been closed.");
}
}catch (Throwable t) {
if (t instanceof InvocationTargetException) {
throw t.getCause() != null ? t.getCause() : t;
} else {
throw t;
}
}
}
public boolean isClosed() {
return connection==null || connection.isDiscarded();
}
public PooledConnection getDelegateConnection() {
return connection;
}
public ConnectionPool getParentPool() {
return pool;
}
@Override
public String toString() {
return "ProxyConnection["+(connection!=null?connection.toString():"null")+"]";
}
}

View File

@@ -0,0 +1,153 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.interceptor.AbstractCreateStatementInterceptor;
public class StatementFacade extends AbstractCreateStatementInterceptor {
private static final Log logger = LogFactory.getLog(StatementFacade.class);
protected StatementFacade(JdbcInterceptor interceptor) {
setUseEquals(interceptor.isUseEquals());
setNext(interceptor);
}
@Override
public void closeInvoked() {
// nothing to do
}
/**
* Creates a statement interceptor to monitor query response times
*/
@Override
public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) {
try {
String name = method.getName();
Constructor<?> constructor = null;
String sql = null;
if (compare(CREATE_STATEMENT, name)) {
// createStatement
constructor = getConstructor(CREATE_STATEMENT_IDX, Statement.class);
} else if (compare(PREPARE_STATEMENT, name)) {
// prepareStatement
constructor = getConstructor(PREPARE_STATEMENT_IDX, PreparedStatement.class);
sql = (String)args[0];
} else if (compare(PREPARE_CALL, name)) {
// prepareCall
constructor = getConstructor(PREPARE_CALL_IDX, CallableStatement.class);
sql = (String)args[0];
} else {
// do nothing
return statement;
}
return constructor.newInstance(new Object[] { new StatementProxy(statement,sql) });
} catch (Exception x) {
logger.warn("Unable to create statement proxy.", x);
}
return statement;
}
/**
* Class to measure query execute time.
*/
protected class StatementProxy implements InvocationHandler {
protected boolean closed = false;
protected Object delegate;
protected final String query;
public StatementProxy(Object parent, String query) {
this.delegate = parent;
this.query = query;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (compare(TOSTRING_VAL,method)) {
return toString();
}
if (compare(EQUALS_VAL, method)) {
return Boolean.valueOf(
this.equals(Proxy.getInvocationHandler(args[0])));
}
if (compare(HASHCODE_VAL, method)) {
return Integer.valueOf(this.hashCode());
}
if (compare(CLOSE_VAL, method)) {
if (delegate == null) return null;
}
if (compare(ISCLOSED_VAL, method)) {
if (delegate == null) return Boolean.TRUE;
}
if (delegate == null) throw new SQLException("Statement closed.");
Object result = null;
try {
//invoke next
result = method.invoke(delegate,args);
} catch (Throwable t) {
if (t instanceof InvocationTargetException && t.getCause() != null) {
throw t.getCause();
} else {
throw t;
}
}
//perform close cleanup
if (compare(CLOSE_VAL, method)) {
delegate = null;
}
return result;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return this==obj;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer(StatementProxy.class.getName());
buf.append("[Proxy=");
buf.append(hashCode());
buf.append("; Query=");
buf.append(query);
buf.append("; Delegate=");
buf.append(delegate);
buf.append("]");
return buf.toString();
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
/**
* Interceptor that traps any unhandled exception types and throws an exception that has been declared by the method
* called, or throw an SQLException if it is declared.
* If the caught exception is not declared, and the method doesn't throw SQLException, then this interceptor will
* throw a RuntimeException
*
*/
public class TrapException extends JdbcInterceptor {
public TrapException() {
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return super.invoke(proxy, method, args);
}catch (Exception t) {
Throwable exception = t;
if (t instanceof InvocationTargetException && t.getCause() != null) {
exception = t.getCause();
if (exception instanceof Error) {
throw exception;
}
}
Class<?> exceptionClass = exception.getClass();
if (!isDeclaredException(method, exceptionClass)) {
if (isDeclaredException(method,SQLException.class)) {
SQLException sqlx = new SQLException("Uncaught underlying exception.");
sqlx.initCause(exception);
exception = sqlx;
} else {
RuntimeException rx = new RuntimeException("Uncaught underlying exception.");
rx.initCause(exception);
exception = rx;
}
}
throw exception;
}
}
public boolean isDeclaredException(Method m, Class<?> clazz) {
for (Class<?> cl : m.getExceptionTypes()) {
if (cl.equals(clazz) || cl.isAssignableFrom(clazz)) return true;
}
return false;
}
/**
* no-op for this interceptor. no state is stored.
*/
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
// NOOP
}
}

View File

@@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import java.sql.Connection;
/**
* Interface to be implemented by custom validator classes.
*
* @author mpassell
*/
public interface Validator {
/**
* Validate a connection and return a boolean to indicate if it's valid.
*
* @param connection the Connection object to test
* @param validateAction the action used. One of {@link PooledConnection#VALIDATE_BORROW},
* {@link PooledConnection#VALIDATE_IDLE}, {@link PooledConnection#VALIDATE_INIT} or
* {@link PooledConnection#VALIDATE_RETURN}
* @return true if the connection is valid
*/
public boolean validate(Connection connection, int validateAction);
}

View File

@@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
public class XADataSource extends DataSource implements javax.sql.XADataSource {
/**
* Constructor for reflection only. A default set of pool properties will be created.
*/
public XADataSource() {
super();
}
/**
* Constructs a DataSource object wrapping a connection
* @param poolProperties The pool configuration
*/
public XADataSource(PoolConfiguration poolProperties) {
super(poolProperties);
}
}

View File

@@ -0,0 +1,166 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
import org.apache.tomcat.jdbc.pool.PooledConnection;
/**
* Abstraction interceptor. This component intercepts all calls to create some type of SQL statement.
* By extending this class, one can intercept queries and update statements by overriding the {@link #createStatement(Object, Method, Object[], Object, long)}
* method.
* @version 1.0
*/
public abstract class AbstractCreateStatementInterceptor extends JdbcInterceptor {
protected static final String CREATE_STATEMENT = "createStatement";
protected static final int CREATE_STATEMENT_IDX = 0;
protected static final String PREPARE_STATEMENT = "prepareStatement";
protected static final int PREPARE_STATEMENT_IDX = 1;
protected static final String PREPARE_CALL = "prepareCall";
protected static final int PREPARE_CALL_IDX = 2;
protected static final String[] STATEMENT_TYPES = {CREATE_STATEMENT, PREPARE_STATEMENT, PREPARE_CALL};
protected static final int STATEMENT_TYPE_COUNT = STATEMENT_TYPES.length;
protected static final String EXECUTE = "execute";
protected static final String EXECUTE_QUERY = "executeQuery";
protected static final String EXECUTE_UPDATE = "executeUpdate";
protected static final String EXECUTE_BATCH = "executeBatch";
protected static final String[] EXECUTE_TYPES = {EXECUTE, EXECUTE_QUERY, EXECUTE_UPDATE, EXECUTE_BATCH};
/**
* the constructors that are used to create statement proxies
*/
protected static final Constructor<?>[] constructors =
new Constructor[AbstractCreateStatementInterceptor.STATEMENT_TYPE_COUNT];
public AbstractCreateStatementInterceptor() {
super();
}
/**
* {@inheritDoc}
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (compare(CLOSE_VAL,method)) {
closeInvoked();
return super.invoke(proxy, method, args);
} else {
boolean process = false;
process = isStatement(method, process);
if (process) {
long start = System.currentTimeMillis();
Object statement = super.invoke(proxy,method,args);
long delta = System.currentTimeMillis() - start;
return createStatement(proxy,method,args,statement, delta);
} else {
return super.invoke(proxy,method,args);
}
}
}
/**
* Creates a constructor for a proxy class, if one doesn't already exist
*
* @param idx
* - the index of the constructor
* @param clazz
* - the interface that the proxy will implement
* @return - returns a constructor used to create new instances
* @throws NoSuchMethodException Constructor not found
*/
protected Constructor<?> getConstructor(int idx, Class<?> clazz) throws NoSuchMethodException {
if (constructors[idx] == null) {
Class<?> proxyClass = Proxy.getProxyClass(AbstractCreateStatementInterceptor.class.getClassLoader(),
new Class[] { clazz });
constructors[idx] = proxyClass.getConstructor(new Class[] { InvocationHandler.class });
}
return constructors[idx];
}
/**
* This method will be invoked after a successful statement creation. This method can choose to return a wrapper
* around the statement or return the statement itself.
* If this method returns a wrapper then it should return a wrapper object that implements one of the following interfaces.
* {@link java.sql.Statement}, {@link java.sql.PreparedStatement} or {@link java.sql.CallableStatement}
* @param proxy the actual proxy object
* @param method the method that was called. It will be one of the methods defined in {@link #STATEMENT_TYPES}
* @param args the arguments to the method
* @param statement the statement that the underlying connection created
* @param time Elapsed time
* @return a {@link java.sql.Statement} object
*/
public abstract Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time);
/**
* Method invoked when the operation {@link java.sql.Connection#close()} is invoked.
*/
public abstract void closeInvoked();
/**
* Returns true if the method that is being invoked matches one of the statement types.
*
* @param method the method being invoked on the proxy
* @param process boolean result used for recursion
* @return returns true if the method name matched
*/
protected boolean isStatement(Method method, boolean process){
return process(STATEMENT_TYPES, method, process);
}
/**
* Returns true if the method that is being invoked matches one of the execute types.
*
* @param method the method being invoked on the proxy
* @param process boolean result used for recursion
* @return returns true if the method name matched
*/
protected boolean isExecute(Method method, boolean process){
return process(EXECUTE_TYPES, method, process);
}
/*
* Returns true if the method that is being invoked matches one of the method names passed in
* @param names list of method names that we want to intercept
* @param method the method being invoked on the proxy
* @param process boolean result used for recursion
* @return returns true if the method name matched
*/
protected boolean process(String[] names, Method method, boolean process) {
final String name = method.getName();
for (int i=0; (!process) && i<names.length; i++) {
process = compare(names[i],name);
}
return process;
}
/**
* no-op for this interceptor. no state is stored.
*/
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
// NOOP
}
}

View File

@@ -0,0 +1,242 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
/**
* Abstract class that wraps statements and intercepts query executions.
*
*/
public abstract class AbstractQueryReport extends AbstractCreateStatementInterceptor {
//logger
private static final Log log = LogFactory.getLog(AbstractQueryReport.class);
/**
* The threshold in milliseconds. If the query is faster than this, we don't measure it
*/
protected long threshold = 1000; //don't report queries less than this
public AbstractQueryReport() {
super();
}
/**
* Invoked when prepareStatement has been called and completed.
* @param sql - the string used to prepare the statement with
* @param time - the time it took to invoke prepare
*/
protected abstract void prepareStatement(String sql, long time);
/**
* Invoked when prepareCall has been called and completed.
* @param query - the string used to prepare the statement with
* @param time - the time it took to invoke prepare
*/
protected abstract void prepareCall(String query, long time);
/**
* Invoked when a query execution, a call to execute/executeQuery or executeBatch failed.
* @param query the query that was executed and failed
* @param args the arguments to the execution
* @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)}
* @param start the time the query execution started
* @param t the exception that happened
* @return - the SQL that was executed or the string &quot;batch&quot; if it was a batch execution
*/
protected String reportFailedQuery(String query, Object[] args, final String name, long start, Throwable t) {
//extract the query string
String sql = (query==null && args!=null && args.length>0)?(String)args[0]:query;
//if we do batch execution, then we name the query 'batch'
if (sql==null && compare(EXECUTE_BATCH,name)) {
sql = "batch";
}
return sql;
}
/**
* Invoked when a query execution, a call to execute/executeQuery or executeBatch succeeded and was within the timing threshold
* @param query the query that was executed and failed
* @param args the arguments to the execution
* @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)}
* @param start the time the query execution started
* @param delta the time the execution took
* @return - the SQL that was executed or the string &quot;batch&quot; if it was a batch execution
*/
protected String reportQuery(String query, Object[] args, final String name, long start, long delta) {
//extract the query string
String sql = (query==null && args!=null && args.length>0)?(String)args[0]:query;
//if we do batch execution, then we name the query 'batch'
if (sql==null && compare(EXECUTE_BATCH,name)) {
sql = "batch";
}
return sql;
}
/**
* Invoked when a query execution, a call to execute/executeQuery or executeBatch succeeded and was exceeded the timing threshold
* @param query the query that was executed and failed
* @param args the arguments to the execution
* @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)}
* @param start the time the query execution started
* @param delta the time the execution took
* @return - the SQL that was executed or the string &quot;batch&quot; if it was a batch execution
*/
protected String reportSlowQuery(String query, Object[] args, final String name, long start, long delta) {
//extract the query string
String sql = (query==null && args!=null && args.length>0)?(String)args[0]:query;
//if we do batch execution, then we name the query 'batch'
if (sql==null && compare(EXECUTE_BATCH,name)) {
sql = "batch";
}
return sql;
}
/**
* returns the query measure threshold.
* This value is in milliseconds. If the query is faster than this threshold than it wont be accounted for
* @return the threshold in milliseconds
*/
public long getThreshold() {
return threshold;
}
/**
* Sets the query measurement threshold. The value is in milliseconds.
* If the query goes faster than this threshold it will not be recorded.
* @param threshold set to -1 to record every query. Value is in milliseconds.
*/
public void setThreshold(long threshold) {
this.threshold = threshold;
}
/**
* Creates a statement interceptor to monitor query response times
*/
@Override
public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) {
try {
Object result = null;
String name = method.getName();
String sql = null;
Constructor<?> constructor = null;
if (compare(CREATE_STATEMENT,name)) {
//createStatement
constructor = getConstructor(CREATE_STATEMENT_IDX,Statement.class);
}else if (compare(PREPARE_STATEMENT,name)) {
//prepareStatement
sql = (String)args[0];
constructor = getConstructor(PREPARE_STATEMENT_IDX,PreparedStatement.class);
if (sql!=null) {
prepareStatement(sql, time);
}
}else if (compare(PREPARE_CALL,name)) {
//prepareCall
sql = (String)args[0];
constructor = getConstructor(PREPARE_CALL_IDX,CallableStatement.class);
prepareCall(sql,time);
}else {
//do nothing, might be a future unsupported method
//so we better bail out and let the system continue
return statement;
}
result = constructor.newInstance(new Object[] { new StatementProxy(statement,sql) });
return result;
}catch (Exception x) {
log.warn("Unable to create statement proxy for slow query report.",x);
}
return statement;
}
/**
* Class to measure query execute time
*
*/
protected class StatementProxy implements InvocationHandler {
protected boolean closed = false;
protected Object delegate;
protected final String query;
public StatementProxy(Object parent, String query) {
this.delegate = parent;
this.query = query;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//get the name of the method for comparison
final String name = method.getName();
//was close invoked?
boolean close = compare(JdbcInterceptor.CLOSE_VAL,name);
//allow close to be called multiple times
if (close && closed) return null;
//are we calling isClosed?
if (compare(JdbcInterceptor.ISCLOSED_VAL,name)) return Boolean.valueOf(closed);
//if we are calling anything else, bail out
if (closed) throw new SQLException("Statement closed.");
boolean process = false;
//check to see if we are about to execute a query
process = isExecute( method, process);
//if we are executing, get the current time
long start = (process)?System.currentTimeMillis():0;
Object result = null;
try {
//execute the query
result = method.invoke(delegate,args);
}catch (Throwable t) {
reportFailedQuery(query,args,name,start,t);
if (t instanceof InvocationTargetException
&& t.getCause() != null) {
throw t.getCause();
} else {
throw t;
}
}
//measure the time
long delta = (process)?(System.currentTimeMillis()-start):Long.MIN_VALUE;
//see if we meet the requirements to measure
if (delta>threshold) {
try {
//report the slow query
reportSlowQuery(query, args, name, start, delta);
}catch (Exception t) {
if (log.isWarnEnabled()) log.warn("Unable to process slow query",t);
}
} else if (process) {
reportQuery(query, args, name, start, delta);
}
//perform close cleanup
if (close) {
closed=true;
delegate = null;
}
return result;
}
}
}

View File

@@ -0,0 +1,164 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.lang.reflect.Method;
import java.sql.SQLException;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.DataSourceFactory;
import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.pool.PooledConnection;
/**
* Interceptor that keep track of connection state to avoid roundtrips to the database.
* The {@link org.apache.tomcat.jdbc.pool.ConnectionPool} is optimized to do as little work as possible.
* The pool itself doesn't remember settings like {@link java.sql.Connection#setAutoCommit(boolean)},
* {@link java.sql.Connection#setReadOnly(boolean)}, {@link java.sql.Connection#setCatalog(String)} or
* {@link java.sql.Connection#setTransactionIsolation(int)}. It relies on the application to remember how and when
* these settings have been applied.
* In the cases where the application code doesn't know or want to keep track of the state, this interceptor helps cache the
* state, and it also avoids roundtrips to the database asking for it.
*
*/
public class ConnectionState extends JdbcInterceptor {
private static final Log log = LogFactory.getLog(ConnectionState.class);
protected final String[] readState = {"getAutoCommit","getTransactionIsolation","isReadOnly","getCatalog"};
protected final String[] writeState = {"setAutoCommit","setTransactionIsolation","setReadOnly","setCatalog"};
protected Boolean autoCommit = null;
protected Integer transactionIsolation = null;
protected Boolean readOnly = null;
protected String catalog = null;
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
if (parent==null || con==null) {
//we are resetting, reset our defaults
autoCommit = null;
transactionIsolation = null;
readOnly = null;
catalog = null;
return;
}
PoolConfiguration poolProperties = parent.getPoolProperties();
if (poolProperties.getDefaultTransactionIsolation()!=DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION) {
try {
if (transactionIsolation==null || transactionIsolation.intValue()!=poolProperties.getDefaultTransactionIsolation()) {
con.getConnection().setTransactionIsolation(poolProperties.getDefaultTransactionIsolation());
transactionIsolation = Integer.valueOf(poolProperties.getDefaultTransactionIsolation());
}
}catch (SQLException x) {
transactionIsolation = null;
log.error("Unable to reset transaction isolation state to connection.",x);
}
}
if (poolProperties.getDefaultReadOnly()!=null) {
try {
if (readOnly==null || readOnly.booleanValue()!=poolProperties.getDefaultReadOnly().booleanValue()) {
con.getConnection().setReadOnly(poolProperties.getDefaultReadOnly().booleanValue());
readOnly = poolProperties.getDefaultReadOnly();
}
}catch (SQLException x) {
readOnly = null;
log.error("Unable to reset readonly state to connection.",x);
}
}
if (poolProperties.getDefaultAutoCommit()!=null) {
try {
if (autoCommit==null || autoCommit.booleanValue()!=poolProperties.getDefaultAutoCommit().booleanValue()) {
con.getConnection().setAutoCommit(poolProperties.getDefaultAutoCommit().booleanValue());
autoCommit = poolProperties.getDefaultAutoCommit();
}
}catch (SQLException x) {
autoCommit = null;
log.error("Unable to reset autocommit state to connection.",x);
}
}
if (poolProperties.getDefaultCatalog()!=null) {
try {
if (catalog==null || (!catalog.equals(poolProperties.getDefaultCatalog()))) {
con.getConnection().setCatalog(poolProperties.getDefaultCatalog());
catalog = poolProperties.getDefaultCatalog();
}
}catch (SQLException x) {
catalog = null;
log.error("Unable to reset default catalog state to connection.",x);
}
}
}
@Override
public void disconnected(ConnectionPool parent, PooledConnection con, boolean finalizing) {
//we are resetting, reset our defaults
autoCommit = null;
transactionIsolation = null;
readOnly = null;
catalog = null;
super.disconnected(parent, con, finalizing);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
boolean read = false;
int index = -1;
for (int i=0; (!read) && i<readState.length; i++) {
read = compare(name,readState[i]);
if (read) index = i;
}
boolean write = false;
for (int i=0; (!write) && (!read) && i<writeState.length; i++) {
write = compare(name,writeState[i]);
if (write) index = i;
}
Object result = null;
if (read) {
switch (index) {
case 0:{result = autoCommit; break;}
case 1:{result = transactionIsolation; break;}
case 2:{result = readOnly; break;}
case 3:{result = catalog; break;}
default: // NOOP
}
//return cached result, if we have it
if (result!=null) return result;
}
result = super.invoke(proxy, method, args);
if (read || write) {
switch (index) {
case 0:{autoCommit = (Boolean) (read?result:args[0]); break;}
case 1:{transactionIsolation = (Integer)(read?result:args[0]); break;}
case 2:{readOnly = (Boolean)(read?result:args[0]); break;}
case 3:{catalog = (String)(read?result:args[0]); break;}
}
}
return result;
}
}

View File

@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
public class QueryTimeoutInterceptor extends AbstractCreateStatementInterceptor {
private static Log log = LogFactory.getLog(QueryTimeoutInterceptor.class);
int timeout = 1;
@Override
public void setProperties(Map<String,InterceptorProperty> properties) {
super.setProperties(properties);
InterceptorProperty p = properties.get("queryTimeout");
if (p!=null) timeout = p.getValueAsInt(timeout);
}
@Override
public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) {
if (statement instanceof Statement && timeout > 0) {
Statement s = (Statement)statement;
try {
s.setQueryTimeout(timeout);
}catch (SQLException x) {
log.warn("[QueryTimeoutInterceptor] Unable to set query timeout:"+x.getMessage(),x);
}
}
return statement;
}
@Override
public void closeInvoked() {
}
}

View File

@@ -0,0 +1,107 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.lang.reflect.Method;
import javax.management.ObjectName;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.PooledConnection;
import org.apache.tomcat.jdbc.pool.jmx.JmxUtil;
/**
* Class that resets the abandoned timer on any activity on the
* Connection or any successful query executions.
* This interceptor is useful for when you have a {@link org.apache.tomcat.jdbc.pool.PoolConfiguration#setRemoveAbandonedTimeout(int)}
* that is fairly low, and you want to reset the abandoned time each time any operation on the connection is performed
* This is useful for batch processing programs that use connections for extensive amount of times.
*
*/
public class ResetAbandonedTimer extends AbstractQueryReport implements ResetAbandonedTimerMBean {
private PooledConnection pcon;
private ObjectName oname = null;
public ResetAbandonedTimer() {
}
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
super.reset(parent, con);
if (con == null) {
this.pcon = null;
if (oname != null) {
JmxUtil.unregisterJmx(oname);
oname = null;
}
} else {
this.pcon = con;
if (oname == null) {
String keyprop = ",JdbcInterceptor=" + getClass().getSimpleName();
oname = JmxUtil.registerJmx(pcon.getObjectName(), keyprop, this);
}
}
}
@Override
public boolean resetTimer() {
boolean result = false;
if (pcon != null) {
pcon.setTimestamp(System.currentTimeMillis());
result = true;
}
return result;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = super.invoke(proxy, method, args);
resetTimer();
return result;
}
@Override
protected void prepareCall(String query, long time) {
resetTimer();
}
@Override
protected void prepareStatement(String sql, long time) {
resetTimer();
}
@Override
public void closeInvoked() {
resetTimer();
}
@Override
protected String reportQuery(String query, Object[] args, String name,long start, long delta) {
resetTimer();
return super.reportQuery(query, args, name, start, delta);
}
@Override
protected String reportSlowQuery(String query, Object[] args, String name,long start, long delta) {
resetTimer();
return super.reportSlowQuery(query, args, name, start, delta);
}
}

View File

@@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
public interface ResetAbandonedTimerMBean {
public boolean resetTimer();
}

View File

@@ -0,0 +1,497 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
import org.apache.tomcat.jdbc.pool.PooledConnection;
/**
* Slow query report interceptor. Tracks timing of query executions.
* @version 1.0
*/
public class SlowQueryReport extends AbstractQueryReport {
//logger
private static final Log log = LogFactory.getLog(SlowQueryReport.class);
/**
* we will be keeping track of query stats on a per pool basis
*/
protected static final ConcurrentHashMap<String,ConcurrentHashMap<String,QueryStats>> perPoolStats =
new ConcurrentHashMap<>();
/**
* the queries that are used for this interceptor.
*/
protected volatile ConcurrentHashMap<String,QueryStats> queries = null;
/**
* Maximum number of queries we will be storing
*/
protected int maxQueries= 1000; //don't store more than this amount of queries
/**
* Flag to enable disable logging of slow queries
*/
protected boolean logSlow = true;
/**
* Flag to enable disable logging of failed queries
*/
protected boolean logFailed = false;
/**
* Sort QueryStats by last invocation time
*/
protected final Comparator<QueryStats> queryStatsComparator = new QueryStatsComparator();
/**
* Returns the query stats for a given pool
* @param poolname - the name of the pool we want to retrieve stats for
* @return a hash map containing statistics for 0 to maxQueries
*/
public static ConcurrentHashMap<String,QueryStats> getPoolStats(String poolname) {
return perPoolStats.get(poolname);
}
/**
* Creates a slow query report interceptor
*/
public SlowQueryReport() {
super();
}
public void setMaxQueries(int maxQueries) {
this.maxQueries = maxQueries;
}
@Override
protected String reportFailedQuery(String query, Object[] args, String name, long start, Throwable t) {
String sql = super.reportFailedQuery(query, args, name, start, t);
if (this.maxQueries > 0 ) {
long now = System.currentTimeMillis();
long delta = now - start;
QueryStats qs = this.getQueryStats(sql);
if (qs != null) {
qs.failure(delta, now);
}
if (isLogFailed() && log.isWarnEnabled()) {
log.warn("Failed Query Report SQL="+sql+"; time="+delta+" ms;");
}
}
return sql;
}
@Override
protected String reportQuery(String query, Object[] args, final String name, long start, long delta) {
String sql = super.reportQuery(query, args, name, start, delta);
if (this.maxQueries > 0 ) {
QueryStats qs = this.getQueryStats(sql);
if (qs != null) qs.add(delta, start);
}
return sql;
}
@Override
protected String reportSlowQuery(String query, Object[] args, String name, long start, long delta) {
String sql = super.reportSlowQuery(query, args, name, start, delta);
if (this.maxQueries > 0 ) {
QueryStats qs = this.getQueryStats(sql);
if (qs != null) {
qs.add(delta, start);
if (isLogSlow() && log.isWarnEnabled()) {
log.warn("Slow Query Report SQL="+sql+"; time="+delta+" ms;");
}
}
}
return sql;
}
/**
* invoked when the connection receives the close request
* Not used for now.
*/
@Override
public void closeInvoked() {
// NOOP
}
@Override
public void prepareStatement(String sql, long time) {
if (this.maxQueries > 0 ) {
QueryStats qs = getQueryStats(sql);
if (qs != null) qs.prepare(time);
}
}
@Override
public void prepareCall(String sql, long time) {
if (this.maxQueries > 0 ) {
QueryStats qs = getQueryStats(sql);
if (qs != null) qs.prepare(time);
}
}
/**
* {@inheritDoc}
*/
@Override
public void poolStarted(ConnectionPool pool) {
super.poolStarted(pool);
//see if we already created a map for this pool
queries = SlowQueryReport.perPoolStats.get(pool.getName());
if (queries==null) {
//create the map to hold our stats
//however TODO we need to improve the eviction
//selection
queries = new ConcurrentHashMap<>();
if (perPoolStats.putIfAbsent(pool.getName(), queries)!=null) {
//there already was one
queries = SlowQueryReport.perPoolStats.get(pool.getName());
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void poolClosed(ConnectionPool pool) {
perPoolStats.remove(pool.getName());
super.poolClosed(pool);
}
protected QueryStats getQueryStats(String sql) {
if (sql==null) sql = "";
ConcurrentHashMap<String,QueryStats> queries = SlowQueryReport.this.queries;
if (queries==null) {
if (log.isWarnEnabled()) log.warn("Connection has already been closed or abandoned");
return null;
}
QueryStats qs = queries.get(sql);
if (qs == null) {
qs = new QueryStats(sql);
if (queries.putIfAbsent(sql,qs)!=null) {
qs = queries.get(sql);
} else {
//we added a new element, see if we need to remove the oldest
if (queries.size() > maxQueries) {
removeOldest(queries);
}
}
}
return qs;
}
/**
* Sort QueryStats by last invocation time
* @param queries The queries map
*/
protected void removeOldest(ConcurrentHashMap<String,QueryStats> queries) {
ArrayList<QueryStats> list = new ArrayList<>(queries.values());
Collections.sort(list, queryStatsComparator);
int removeIndex = 0;
while (queries.size() > maxQueries) {
String sql = list.get(removeIndex).getQuery();
queries.remove(sql);
if (log.isDebugEnabled()) log.debug("Removing slow query, capacity reached:"+sql);
removeIndex++;
}
}
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
super.reset(parent, con);
if (parent!=null)
queries = SlowQueryReport.perPoolStats.get(parent.getName());
else
queries = null;
}
public boolean isLogSlow() {
return logSlow;
}
public void setLogSlow(boolean logSlow) {
this.logSlow = logSlow;
}
public boolean isLogFailed() {
return logFailed;
}
public void setLogFailed(boolean logFailed) {
this.logFailed = logFailed;
}
@Override
public void setProperties(Map<String, InterceptorProperty> properties) {
super.setProperties(properties);
final String threshold = "threshold";
final String maxqueries= "maxQueries";
final String logslow = "logSlow";
final String logfailed = "logFailed";
InterceptorProperty p1 = properties.get(threshold);
InterceptorProperty p2 = properties.get(maxqueries);
InterceptorProperty p3 = properties.get(logslow);
InterceptorProperty p4 = properties.get(logfailed);
if (p1!=null) {
setThreshold(Long.parseLong(p1.getValue()));
}
if (p2!=null) {
setMaxQueries(Integer.parseInt(p2.getValue()));
}
if (p3!=null) {
setLogSlow(Boolean.parseBoolean(p3.getValue()));
}
if (p4!=null) {
setLogFailed(Boolean.parseBoolean(p4.getValue()));
}
}
public static class QueryStats {
static final String[] FIELD_NAMES = new String[] {
"query",
"nrOfInvocations",
"maxInvocationTime",
"maxInvocationDate",
"minInvocationTime",
"minInvocationDate",
"totalInvocationTime",
"failures",
"prepareCount",
"prepareTime",
"lastInvocation"
};
static final String[] FIELD_DESCRIPTIONS = new String[] {
"The SQL query",
"The number of query invocations, a call to executeXXX",
"The longest time for this query in milliseconds",
"The time and date for when the longest query took place",
"The shortest time for this query in milliseconds",
"The time and date for when the shortest query took place",
"The total amount of milliseconds spent executing this query",
"The number of failures for this query",
"The number of times this query was prepared (prepareStatement/prepareCall)",
"The total number of milliseconds spent preparing this query",
"The date and time of the last invocation"
};
static final OpenType<?>[] FIELD_TYPES = new OpenType[] {
SimpleType.STRING,
SimpleType.INTEGER,
SimpleType.LONG,
SimpleType.LONG,
SimpleType.LONG,
SimpleType.LONG,
SimpleType.LONG,
SimpleType.LONG,
SimpleType.INTEGER,
SimpleType.LONG,
SimpleType.LONG
};
private final String query;
private volatile int nrOfInvocations;
private volatile long maxInvocationTime = Long.MIN_VALUE;
private volatile long maxInvocationDate;
private volatile long minInvocationTime = Long.MAX_VALUE;
private volatile long minInvocationDate;
private volatile long totalInvocationTime;
private volatile long failures;
private volatile int prepareCount;
private volatile long prepareTime;
private volatile long lastInvocation = 0;
public static String[] getFieldNames() {
return FIELD_NAMES;
}
public static String[] getFieldDescriptions() {
return FIELD_DESCRIPTIONS;
}
public static OpenType<?>[] getFieldTypes() {
return FIELD_TYPES;
}
@Override
public String toString() {
SimpleDateFormat sdf =
new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
StringBuilder buf = new StringBuilder("QueryStats[query:");
buf.append(query);
buf.append(", nrOfInvocations:");
buf.append(nrOfInvocations);
buf.append(", maxInvocationTime:");
buf.append(maxInvocationTime);
buf.append(", maxInvocationDate:");
buf.append(sdf.format(new java.util.Date(maxInvocationDate)));
buf.append(", minInvocationTime:");
buf.append(minInvocationTime);
buf.append(", minInvocationDate:");
buf.append(sdf.format(new java.util.Date(minInvocationDate)));
buf.append(", totalInvocationTime:");
buf.append(totalInvocationTime);
buf.append(", averageInvocationTime:");
buf.append((float)totalInvocationTime / (float)nrOfInvocations);
buf.append(", failures:");
buf.append(failures);
buf.append(", prepareCount:");
buf.append(prepareCount);
buf.append(", prepareTime:");
buf.append(prepareTime);
buf.append("]");
return buf.toString();
}
public CompositeDataSupport getCompositeData(final CompositeType type) throws OpenDataException{
Object[] values = new Object[] {
query,
Integer.valueOf(nrOfInvocations),
Long.valueOf(maxInvocationTime),
Long.valueOf(maxInvocationDate),
Long.valueOf(minInvocationTime),
Long.valueOf(minInvocationDate),
Long.valueOf(totalInvocationTime),
Long.valueOf(failures),
Integer.valueOf(prepareCount),
Long.valueOf(prepareTime),
Long.valueOf(lastInvocation)
};
return new CompositeDataSupport(type,FIELD_NAMES,values);
}
public QueryStats(String query) {
this.query = query;
}
public void prepare(long invocationTime) {
prepareCount++;
prepareTime+=invocationTime;
}
public void add(long invocationTime, long now) {
//not thread safe, but don't sacrifice performance for this kind of stuff
maxInvocationTime = Math.max(invocationTime, maxInvocationTime);
if (maxInvocationTime == invocationTime) {
maxInvocationDate = now;
}
minInvocationTime = Math.min(invocationTime, minInvocationTime);
if (minInvocationTime==invocationTime) {
minInvocationDate = now;
}
nrOfInvocations++;
totalInvocationTime+=invocationTime;
lastInvocation = now;
}
public void failure(long invocationTime, long now) {
add(invocationTime,now);
failures++;
}
public String getQuery() {
return query;
}
public int getNrOfInvocations() {
return nrOfInvocations;
}
public long getMaxInvocationTime() {
return maxInvocationTime;
}
public long getMaxInvocationDate() {
return maxInvocationDate;
}
public long getMinInvocationTime() {
return minInvocationTime;
}
public long getMinInvocationDate() {
return minInvocationDate;
}
public long getTotalInvocationTime() {
return totalInvocationTime;
}
@Override
public int hashCode() {
return query.hashCode();
}
@Override
public boolean equals(Object other) {
if (other instanceof QueryStats) {
QueryStats qs = (QueryStats)other;
return qs.query.equals(this.query);
}
return false;
}
public boolean isOlderThan(QueryStats other) {
return this.lastInvocation < other.lastInvocation;
}
}
/** Compare QueryStats by their lastInvocation value. QueryStats that
* have never been updated, have a lastInvocation value of {@code 0}
* which should be handled as the newest possible invocation.
*/
// Public for unit tests
public static class QueryStatsComparator implements Comparator<QueryStats> {
@Override
public int compare(QueryStats stats1, QueryStats stats2) {
return Long.compare(handleZero(stats1.lastInvocation),
handleZero(stats2.lastInvocation));
}
private static long handleZero(long value) {
return value == 0 ? Long.MAX_VALUE : value;
}
}
}

View File

@@ -0,0 +1,301 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.RuntimeOperationsException;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
import org.apache.tomcat.jdbc.pool.PooledConnection;
import org.apache.tomcat.jdbc.pool.jmx.JmxUtil;
/**
* Publishes data to JMX and provides notifications
* when failures happen.
*
*/
public class SlowQueryReportJmx extends SlowQueryReport implements NotificationEmitter, SlowQueryReportJmxMBean{
public static final String SLOW_QUERY_NOTIFICATION = "SLOW QUERY";
public static final String FAILED_QUERY_NOTIFICATION = "FAILED QUERY";
public static final String objectNameAttribute = "objectName";
protected static volatile CompositeType SLOW_QUERY_TYPE;
private static final Log log = LogFactory.getLog(SlowQueryReportJmx.class);
protected static final ConcurrentHashMap<String,SlowQueryReportJmxMBean> mbeans =
new ConcurrentHashMap<>();
//==============================JMX STUFF========================
protected volatile NotificationBroadcasterSupport notifier = new NotificationBroadcasterSupport();
@Override
public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException {
notifier.addNotificationListener(listener, filter, handback);
}
@Override
public MBeanNotificationInfo[] getNotificationInfo() {
return notifier.getNotificationInfo();
}
@Override
public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
notifier.removeNotificationListener(listener);
}
@Override
public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException {
notifier.removeNotificationListener(listener, filter, handback);
}
//==============================JMX STUFF========================
protected String poolName = null;
protected static final AtomicLong notifySequence = new AtomicLong(0);
protected boolean notifyPool = true;
protected ConnectionPool pool = null;
protected static CompositeType getCompositeType() {
if (SLOW_QUERY_TYPE==null) {
try {
SLOW_QUERY_TYPE = new CompositeType(
SlowQueryReportJmx.class.getName(),
"Composite data type for query statistics",
QueryStats.getFieldNames(),
QueryStats.getFieldDescriptions(),
QueryStats.getFieldTypes());
}catch (OpenDataException x) {
log.warn("Unable to initialize composite data type for JMX stats and notifications.",x);
}
}
return SLOW_QUERY_TYPE;
}
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
super.reset(parent, con);
if (parent!=null) {
poolName = parent.getName();
pool = parent;
registerJmx();
}
}
@Override
public void poolClosed(ConnectionPool pool) {
this.poolName = pool.getName();
deregisterJmx();
super.poolClosed(pool);
}
@Override
public void poolStarted(ConnectionPool pool) {
this.pool = pool;
super.poolStarted(pool);
this.poolName = pool.getName();
}
@Override
protected String reportFailedQuery(String query, Object[] args, String name, long start, Throwable t) {
query = super.reportFailedQuery(query, args, name, start, t);
if (isLogFailed()) notifyJmx(query,FAILED_QUERY_NOTIFICATION);
return query;
}
protected void notifyJmx(String query, String type) {
try {
long sequence = notifySequence.incrementAndGet();
if (isNotifyPool()) {
if (this.pool!=null && this.pool.getJmxPool()!=null) {
this.pool.getJmxPool().notify(type, query);
}
} else {
if (notifier!=null) {
Notification notification =
new Notification(type,
this,
sequence,
System.currentTimeMillis(),
query);
notifier.sendNotification(notification);
}
}
} catch (RuntimeOperationsException e) {
if (log.isDebugEnabled()) {
log.debug("Unable to send failed query notification.",e);
}
}
}
@Override
protected String reportSlowQuery(String query, Object[] args, String name, long start, long delta) {
query = super.reportSlowQuery(query, args, name, start, delta);
if (isLogSlow()) notifyJmx(query,SLOW_QUERY_NOTIFICATION);
return query;
}
/**
* JMX operation - return the names of all the pools
* @return - all the names of pools that we have stored data for
*/
public String[] getPoolNames() {
Set<String> keys = perPoolStats.keySet();
return keys.toArray(new String[0]);
}
/**
* JMX operation - return the name of the pool
* @return the name of the pool, unique within the JVM
*/
public String getPoolName() {
return poolName;
}
public boolean isNotifyPool() {
return notifyPool;
}
public void setNotifyPool(boolean notifyPool) {
this.notifyPool = notifyPool;
}
/**
* JMX operation - remove all stats for this connection pool
*/
public void resetStats() {
ConcurrentHashMap<String,QueryStats> queries = perPoolStats.get(poolName);
if (queries!=null) {
Iterator<String> it = queries.keySet().iterator();
while (it.hasNext()) it.remove();
}
}
/**
* JMX operation - returns all the queries we have collected.
* @return - the slow query report as composite data.
*/
@Override
public CompositeData[] getSlowQueriesCD() throws OpenDataException {
CompositeDataSupport[] result = null;
ConcurrentHashMap<String,QueryStats> queries = perPoolStats.get(poolName);
if (queries!=null) {
Set<Map.Entry<String,QueryStats>> stats = queries.entrySet();
if (stats!=null) {
result = new CompositeDataSupport[stats.size()];
Iterator<Map.Entry<String,QueryStats>> it = stats.iterator();
int pos = 0;
while (it.hasNext()) {
Map.Entry<String,QueryStats> entry = it.next();
QueryStats qs = entry.getValue();
result[pos++] = qs.getCompositeData(getCompositeType());
}
}
}
return result;
}
protected void deregisterJmx() {
try {
if (mbeans.remove(poolName)!=null) {
ObjectName oname = getObjectName(getClass(),poolName);
JmxUtil.unregisterJmx(oname);
}
} catch (MalformedObjectNameException e) {
log.warn("Jmx deregistration failed.",e);
} catch (RuntimeOperationsException e) {
log.warn("Jmx deregistration failed.",e);
}
}
public ObjectName getObjectName(Class<?> clazz, String poolName) throws MalformedObjectNameException {
ObjectName oname;
Map<String,InterceptorProperty> properties = getProperties();
if (properties != null && properties.containsKey(objectNameAttribute)) {
oname = new ObjectName(properties.get(objectNameAttribute).getValue());
} else {
oname = new ObjectName(ConnectionPool.POOL_JMX_TYPE_PREFIX+clazz.getName()+",name=" + poolName);
}
return oname;
}
protected void registerJmx() {
try {
//only if we notify the pool itself
if (isNotifyPool()) {
} else if (getCompositeType()!=null) {
ObjectName oname = getObjectName(getClass(),poolName);
if (mbeans.putIfAbsent(poolName, this)==null) {
JmxUtil.registerJmx(oname, null, this);
}
} else {
log.warn(SlowQueryReport.class.getName()+ "- No JMX support, composite type was not found.");
}
} catch (MalformedObjectNameException e) {
log.error("Jmx registration failed, no JMX data will be exposed for the query stats.",e);
} catch (RuntimeOperationsException e) {
log.error("Jmx registration failed, no JMX data will be exposed for the query stats.",e);
}
}
@Override
public void setProperties(Map<String, InterceptorProperty> properties) {
super.setProperties(properties);
final String threshold = "notifyPool";
InterceptorProperty p1 = properties.get(threshold);
if (p1!=null) {
this.setNotifyPool(Boolean.parseBoolean(p1.getValue()));
}
}
}

View File

@@ -0,0 +1,23 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.OpenDataException;
public interface SlowQueryReportJmxMBean {
public CompositeData[] getSlowQueriesCD() throws OpenDataException;
}

View File

@@ -0,0 +1,388 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
import org.apache.tomcat.jdbc.pool.PooledConnection;
import org.apache.tomcat.jdbc.pool.jmx.JmxUtil;
/**
* Interceptor that caches {@code PreparedStatement} and/or
* {@code CallableStatement} instances on a connection.
*/
public class StatementCache extends StatementDecoratorInterceptor implements StatementCacheMBean {
private static final Log log = LogFactory.getLog(StatementCache.class);
protected static final String[] ALL_TYPES = new String[] {PREPARE_STATEMENT,PREPARE_CALL};
protected static final String[] CALLABLE_TYPE = new String[] {PREPARE_CALL};
protected static final String[] PREPARED_TYPE = new String[] {PREPARE_STATEMENT};
protected static final String[] NO_TYPE = new String[] {};
protected static final String STATEMENT_CACHE_ATTR = StatementCache.class.getName() + ".cache";
/*begin properties for the statement cache*/
private boolean cachePrepared = true;
private boolean cacheCallable = false;
private int maxCacheSize = 50;
private PooledConnection pcon;
private String[] types;
private ObjectName oname = null;
@Override
public boolean isCachePrepared() {
return cachePrepared;
}
@Override
public boolean isCacheCallable() {
return cacheCallable;
}
@Override
public int getMaxCacheSize() {
return maxCacheSize;
}
public String[] getTypes() {
return types;
}
@Override
public AtomicInteger getCacheSize() {
return cacheSize;
}
@Override
public void setProperties(Map<String, InterceptorProperty> properties) {
super.setProperties(properties);
InterceptorProperty p = properties.get("prepared");
if (p!=null) cachePrepared = p.getValueAsBoolean(cachePrepared);
p = properties.get("callable");
if (p!=null) cacheCallable = p.getValueAsBoolean(cacheCallable);
p = properties.get("max");
if (p!=null) maxCacheSize = p.getValueAsInt(maxCacheSize);
if (cachePrepared && cacheCallable) {
this.types = ALL_TYPES;
} else if (cachePrepared) {
this.types = PREPARED_TYPE;
} else if (cacheCallable) {
this.types = CALLABLE_TYPE;
} else {
this.types = NO_TYPE;
}
}
/*end properties for the statement cache*/
/*begin the cache size*/
private static ConcurrentHashMap<ConnectionPool,AtomicInteger> cacheSizeMap =
new ConcurrentHashMap<>();
private AtomicInteger cacheSize;
@Override
public void poolStarted(ConnectionPool pool) {
cacheSizeMap.putIfAbsent(pool, new AtomicInteger(0));
super.poolStarted(pool);
}
@Override
public void poolClosed(ConnectionPool pool) {
cacheSizeMap.remove(pool);
super.poolClosed(pool);
}
/*end the cache size*/
/*begin the actual statement cache*/
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
super.reset(parent, con);
if (parent==null) {
cacheSize = null;
this.pcon = null;
if (oname != null) {
JmxUtil.unregisterJmx(oname);
oname = null;
}
} else {
cacheSize = cacheSizeMap.get(parent);
this.pcon = con;
if (!pcon.getAttributes().containsKey(STATEMENT_CACHE_ATTR)) {
ConcurrentHashMap<CacheKey,CachedStatement> cache =
new ConcurrentHashMap<>();
pcon.getAttributes().put(STATEMENT_CACHE_ATTR,cache);
}
if (oname == null) {
String keyprop = ",JdbcInterceptor=" + getClass().getSimpleName();
oname = JmxUtil.registerJmx(pcon.getObjectName(), keyprop, this);
}
}
}
@Override
public void disconnected(ConnectionPool parent, PooledConnection con, boolean finalizing) {
@SuppressWarnings("unchecked")
ConcurrentHashMap<CacheKey,CachedStatement> statements =
(ConcurrentHashMap<CacheKey,CachedStatement>)con.getAttributes().get(STATEMENT_CACHE_ATTR);
if (statements!=null) {
for (Map.Entry<CacheKey, CachedStatement> p : statements.entrySet()) {
closeStatement(p.getValue());
}
statements.clear();
}
super.disconnected(parent, con, finalizing);
}
public void closeStatement(CachedStatement st) {
if (st==null) return;
st.forceClose();
}
@Override
protected Object createDecorator(Object proxy, Method method, Object[] args,
Object statement, Constructor<?> constructor, String sql)
throws InstantiationException, IllegalAccessException, InvocationTargetException {
boolean process = process(this.types, method, false);
if (process) {
Object result = null;
CachedStatement statementProxy = new CachedStatement((PreparedStatement)statement,sql);
result = constructor.newInstance(new Object[] { statementProxy });
statementProxy.setActualProxy(result);
statementProxy.setConnection(proxy);
statementProxy.setConstructor(constructor);
statementProxy.setCacheKey(createCacheKey(method, args));
return result;
} else {
return super.createDecorator(proxy, method, args, statement, constructor, sql);
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
boolean process = process(this.types, method, false);
if (process && args.length>0 && args[0] instanceof String) {
CachedStatement statement = isCached(method, args);
if (statement!=null) {
//remove it from the cache since it is used
removeStatement(statement);
return statement.getActualProxy();
} else {
return super.invoke(proxy, method, args);
}
} else {
return super.invoke(proxy,method,args);
}
}
/**
* @param sql The SQL to attempt to match to entries in the statement cache
*
* @return The CachedStatement for the given SQL
*
* @deprecated Unused. Will be removed in Tomcat 9
*/
@Deprecated
public CachedStatement isCached(String sql) {
return null;
}
public CachedStatement isCached(Method method, Object[] args) {
ConcurrentHashMap<CacheKey,CachedStatement> cache = getCache();
if (cache == null) return null;
return cache.get(createCacheKey(method, args));
}
public boolean cacheStatement(CachedStatement proxy) {
ConcurrentHashMap<CacheKey,CachedStatement> cache = getCache();
if (cache == null) return false;
if (proxy.getCacheKey()==null) {
return false;
} else if (cache.containsKey(proxy.getCacheKey())) {
return false;
} else if (cacheSize.get()>=maxCacheSize) {
return false;
} else if (cacheSize.incrementAndGet()>maxCacheSize) {
cacheSize.decrementAndGet();
return false;
} else {
//cache the statement
cache.put(proxy.getCacheKey(), proxy);
return true;
}
}
public boolean removeStatement(CachedStatement proxy) {
ConcurrentHashMap<CacheKey,CachedStatement> cache = getCache();
if (cache == null) return false;
if (cache.remove(proxy.getCacheKey()) != null) {
cacheSize.decrementAndGet();
return true;
} else {
return false;
}
}
/*end the actual statement cache*/
protected ConcurrentHashMap<CacheKey,CachedStatement> getCache() {
PooledConnection pCon = this.pcon;
if (pCon == null) {
if (log.isWarnEnabled()) log.warn("Connection has already been closed or abandoned");
return null;
}
@SuppressWarnings("unchecked")
ConcurrentHashMap<CacheKey,CachedStatement> cache =
(ConcurrentHashMap<CacheKey,CachedStatement>)pCon.getAttributes().get(STATEMENT_CACHE_ATTR);
return cache;
}
@Override
public int getCacheSizePerConnection() {
ConcurrentHashMap<CacheKey,CachedStatement> cache = getCache();
if (cache == null) return 0;
return cache.size();
}
protected class CachedStatement extends StatementDecoratorInterceptor.StatementProxy<PreparedStatement> {
boolean cached = false;
CacheKey key;
public CachedStatement(PreparedStatement parent, String sql) {
super(parent, sql);
}
@Override
public void closeInvoked() {
//should we cache it
boolean shouldClose = true;
if (cacheSize.get() < maxCacheSize) {
//cache a proxy so that we don't reuse the facade
CachedStatement proxy = new CachedStatement(getDelegate(),getSql());
proxy.setCacheKey(getCacheKey());
try {
// clear Resultset
ResultSet result = getDelegate().getResultSet();
if (result != null && !result.isClosed()) {
result.close();
}
// clear parameter
getDelegate().clearParameters();
//create a new facade
Object actualProxy = getConstructor().newInstance(new Object[] { proxy });
proxy.setActualProxy(actualProxy);
proxy.setConnection(getConnection());
proxy.setConstructor(getConstructor());
if (cacheStatement(proxy)) {
proxy.cached = true;
shouldClose = false;
}
} catch (RuntimeException | ReflectiveOperationException | SQLException x) {
removeStatement(proxy);
}
}
if (shouldClose) {
super.closeInvoked();
}
closed = true;
delegate = null;
}
public void forceClose() {
removeStatement(this);
super.closeInvoked();
}
public CacheKey getCacheKey() {
return key;
}
public void setCacheKey(CacheKey cacheKey) {
key = cacheKey;
}
}
protected CacheKey createCacheKey(Method method, Object[] args) {
return createCacheKey(method.getName(), args);
}
protected CacheKey createCacheKey(String methodName, Object[] args) {
CacheKey key = null;
if (compare(PREPARE_STATEMENT, methodName)) {
key = new CacheKey(PREPARE_STATEMENT, args);
} else if (compare(PREPARE_CALL, methodName)) {
key = new CacheKey(PREPARE_CALL, args);
}
return key;
}
private static final class CacheKey {
private final String stmtType;
private final Object[] args;
private CacheKey(String type, Object[] methodArgs) {
stmtType = type;
args = methodArgs;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.deepHashCode(args);
result = prime * result
+ ((stmtType == null) ? 0 : stmtType.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CacheKey other = (CacheKey) obj;
if (!Arrays.deepEquals(args, other.args))
return false;
if (stmtType == null) {
if (other.stmtType != null)
return false;
} else if (!stmtType.equals(other.stmtType))
return false;
return true;
}
}
}

View File

@@ -0,0 +1,26 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.util.concurrent.atomic.AtomicInteger;
public interface StatementCacheMBean {
public boolean isCachePrepared();
public boolean isCacheCallable();
public int getMaxCacheSize();
public AtomicInteger getCacheSize();
public int getCacheSizePerConnection();
}

View File

@@ -0,0 +1,298 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/**
* Implementation of <b>JdbcInterceptor</b> that proxies resultSets and statements.
* @author Guillermo Fernandes
*/
public class StatementDecoratorInterceptor extends AbstractCreateStatementInterceptor {
private static final Log logger = LogFactory.getLog(StatementDecoratorInterceptor.class);
protected static final String EXECUTE_QUERY = "executeQuery";
protected static final String GET_GENERATED_KEYS = "getGeneratedKeys";
protected static final String GET_RESULTSET = "getResultSet";
protected static final String[] RESULTSET_TYPES = {EXECUTE_QUERY, GET_GENERATED_KEYS, GET_RESULTSET};
/**
* the constructor to create the resultSet proxies
*/
protected static volatile Constructor<?> resultSetConstructor = null;
@Override
public void closeInvoked() {
// nothing to do
}
protected Constructor<?> getResultSetConstructor() throws NoSuchMethodException {
if (resultSetConstructor == null) {
Class<?> proxyClass = Proxy.getProxyClass(StatementDecoratorInterceptor.class.getClassLoader(),
new Class[] { ResultSet.class });
resultSetConstructor = proxyClass.getConstructor(new Class[] { InvocationHandler.class });
}
return resultSetConstructor;
}
/**
* Creates a statement interceptor to monitor query response times
*/
@Override
public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) {
try {
String name = method.getName();
Constructor<?> constructor = null;
String sql = null;
if (compare(CREATE_STATEMENT, name)) {
// createStatement
constructor = getConstructor(CREATE_STATEMENT_IDX, Statement.class);
} else if (compare(PREPARE_STATEMENT, name)) {
// prepareStatement
constructor = getConstructor(PREPARE_STATEMENT_IDX, PreparedStatement.class);
sql = (String)args[0];
} else if (compare(PREPARE_CALL, name)) {
// prepareCall
constructor = getConstructor(PREPARE_CALL_IDX, CallableStatement.class);
sql = (String)args[0];
} else {
// do nothing, might be a future unsupported method
// so we better bail out and let the system continue
return statement;
}
return createDecorator(proxy, method, args, statement, constructor, sql);
} catch (Exception x) {
if (x instanceof InvocationTargetException) {
Throwable cause = x.getCause();
if (cause instanceof ThreadDeath) {
throw (ThreadDeath) cause;
}
if (cause instanceof VirtualMachineError) {
throw (VirtualMachineError) cause;
}
}
logger.warn("Unable to create statement proxy for slow query report.", x);
}
return statement;
}
/**
* Creates a proxy for a Statement.
*
* @param proxy The proxy object on which the method that triggered
* the creation of the statement was called.
* @param method The method that was called on the proxy
* @param args The arguments passed as part of the method call to
* the proxy
* @param statement The statement object that is to be proxied
* @param constructor The constructor for the desired proxy
* @param sql The sql of of the statement
*
* @return A new proxy for the Statement
* @throws InstantiationException Couldn't instantiate object
* @throws IllegalAccessException Inaccessible constructor
* @throws InvocationTargetException Exception thrown from constructor
*/
protected Object createDecorator(Object proxy, Method method, Object[] args,
Object statement, Constructor<?> constructor, String sql)
throws InstantiationException, IllegalAccessException, InvocationTargetException {
Object result = null;
StatementProxy<Statement> statementProxy =
new StatementProxy<>((Statement)statement,sql);
result = constructor.newInstance(new Object[] { statementProxy });
statementProxy.setActualProxy(result);
statementProxy.setConnection(proxy);
statementProxy.setConstructor(constructor);
return result;
}
protected boolean isExecuteQuery(String methodName) {
return EXECUTE_QUERY.equals(methodName);
}
protected boolean isExecuteQuery(Method method) {
return isExecuteQuery(method.getName());
}
protected boolean isResultSet(Method method, boolean process) {
return process(RESULTSET_TYPES, method, process);
}
/**
* Class to measure query execute time.
*/
protected class StatementProxy<T extends java.sql.Statement> implements InvocationHandler {
protected boolean closed = false;
protected T delegate;
private Object actualProxy;
private Object connection;
private String sql;
private Constructor<?> constructor;
public StatementProxy(T delegate, String sql) {
this.delegate = delegate;
this.sql = sql;
}
public T getDelegate() {
return this.delegate;
}
public String getSql() {
return sql;
}
public void setConnection(Object proxy) {
this.connection = proxy;
}
public Object getConnection() {
return this.connection;
}
public void setActualProxy(Object proxy){
this.actualProxy = proxy;
}
public Object getActualProxy() {
return this.actualProxy;
}
public Constructor<?> getConstructor() {
return constructor;
}
public void setConstructor(Constructor<?> constructor) {
this.constructor = constructor;
}
public void closeInvoked() {
if (getDelegate()!=null) {
try {
getDelegate().close();
}catch (SQLException ignore) {
}
}
closed = true;
delegate = null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (compare(TOSTRING_VAL,method)) {
return toString();
}
// was close invoked?
boolean close = compare(CLOSE_VAL, method);
// allow close to be called multiple times
if (close && closed)
return null;
// are we calling isClosed?
if (compare(ISCLOSED_VAL, method))
return Boolean.valueOf(closed);
// if we are calling anything else, bail out
if (closed)
throw new SQLException("Statement closed.");
if (compare(GETCONNECTION_VAL,method)){
return connection;
}
boolean process = false;
process = isResultSet(method, process);
// check to see if we are about to execute a query
// if we are executing, get the current time
Object result = null;
try {
// perform close cleanup
if (close) {
closeInvoked();
} else {
// execute the query
result = method.invoke(delegate, args);
}
} catch (Throwable t) {
if (t instanceof InvocationTargetException
&& t.getCause() != null) {
throw t.getCause();
} else {
throw t;
}
}
if (process && result != null) {
Constructor<?> cons = getResultSetConstructor();
result = cons.newInstance(new Object[]{new ResultSetProxy(actualProxy, result)});
}
return result;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer(StatementProxy.class.getName());
buf.append("[Proxy=");
buf.append(System.identityHashCode(this));
buf.append("; Sql=");
buf.append(getSql());
buf.append("; Delegate=");
buf.append(getDelegate());
buf.append("; Connection=");
buf.append(getConnection());
buf.append("]");
return buf.toString();
}
}
protected class ResultSetProxy implements InvocationHandler {
private Object st;
private Object delegate;
public ResultSetProxy(Object st, Object delegate) {
this.st = st;
this.delegate = delegate;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("getStatement")) {
return this.st;
} else {
try {
return method.invoke(this.delegate, args);
} catch (Throwable t) {
if (t instanceof InvocationTargetException
&& t.getCause() != null) {
throw t.getCause();
} else {
throw t;
}
}
}
}
}
}

View File

@@ -0,0 +1,116 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.sql.Statement;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.apache.tomcat.jdbc.pool.PooledConnection;
/**
* Keeps track of statements associated with a connection and invokes close upon {@link java.sql.Connection#close()}
* Useful for applications that dont close the associated statements after being done with a connection.
*
*/
public class StatementFinalizer extends AbstractCreateStatementInterceptor {
private static final Log log = LogFactory.getLog(StatementFinalizer.class);
protected List<StatementEntry> statements = new LinkedList<>();
private boolean logCreationStack = false;
@Override
public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) {
try {
if (statement instanceof Statement)
statements.add(new StatementEntry((Statement)statement));
}catch (ClassCastException x) {
//ignore this one
}
return statement;
}
@SuppressWarnings("null") // st is not null when used
@Override
public void closeInvoked() {
while (!statements.isEmpty()) {
StatementEntry ws = statements.remove(0);
Statement st = ws.getStatement();
boolean shallClose = false;
try {
shallClose = st!=null && (!st.isClosed());
if (shallClose) {
st.close();
}
} catch (Exception ignore) {
if (log.isDebugEnabled()) {
log.debug("Unable to closed statement upon connection close.",ignore);
}
} finally {
if (logCreationStack && shallClose) {
log.warn("Statement created, but was not closed at:", ws.getAllocationStack());
}
}
}
}
@Override
public void setProperties(Map<String, PoolProperties.InterceptorProperty> properties) {
super.setProperties(properties);
PoolProperties.InterceptorProperty logProperty = properties.get("trace");
if (null != logProperty) {
logCreationStack = logProperty.getValueAsBoolean(logCreationStack);
}
}
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
statements.clear();
super.reset(parent, con);
}
protected class StatementEntry {
private WeakReference<Statement> statement;
private Throwable allocationStack;
public StatementEntry(Statement statement) {
this.statement = new WeakReference<>(statement);
if (logCreationStack) {
this.allocationStack = new Throwable();
}
}
public Statement getStatement() {
return statement.get();
}
public Throwable getAllocationStack() {
return allocationStack;
}
}
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<mbeans-descriptors>
<mbean description="Reports " domain="tomcat.jdbc" group="jdbc-pool" name="SlowQueryReportJmx"
type="org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx">
<attribute description="The name of the connection pool this Jmx bean is representing" name="poolName" type="java.lang.String" writeable="false"/>
<attribute description="List of all registered connections pools" name="poolNames" type="[java.lang.String;" writeable="false"/>
<attribute description="All the recorded query stats. " name="slowQueriesCD" type="[javax.management.openmbean.CompositeData;" writeable="false"/>
<operation description="Clears all the query stats" impact="ACTION" name="resetStats" returnType="void"/>
<notification description="Notification sent out by the slow query report when a query exceeds the threshold" name="slow-query">
<notification-type>Slow query</notification-type>
</notification>
<notification description="Notification sent out by the slow query report when a query fails execution" name="failed-query">
<notification-type>Failed query execution</notification-type>
</notification>
</mbean>
</mbeans-descriptors>

View File

@@ -0,0 +1,984 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.jmx;
import java.util.Properties;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorDefinition;
import org.apache.tomcat.jdbc.pool.PoolUtilities;
import org.apache.tomcat.jdbc.pool.Validator;
public class ConnectionPool extends NotificationBroadcasterSupport
implements ConnectionPoolMBean, MBeanRegistration {
/**
* logger
*/
private static final Log log = LogFactory.getLog(ConnectionPool.class);
/**
* the connection pool
*/
protected org.apache.tomcat.jdbc.pool.ConnectionPool pool = null;
/**
* sequence for JMX notifications
*/
protected AtomicInteger sequence = new AtomicInteger(0);
/**
* Listeners that are local and interested in our notifications, no need for JMX
*/
protected ConcurrentLinkedQueue<NotificationListener> listeners =
new ConcurrentLinkedQueue<>();
/**
* the ObjectName of this pool.
*/
private ObjectName oname = null;
public ConnectionPool(org.apache.tomcat.jdbc.pool.ConnectionPool pool) {
super();
this.pool = pool;
}
public org.apache.tomcat.jdbc.pool.ConnectionPool getPool() {
return pool;
}
public PoolConfiguration getPoolProperties() {
return pool.getPoolProperties();
}
public ObjectName getObjectName() {
return oname;
}
@Override
public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception {
this.oname = name;
return name;
}
@Override
public void postRegister(Boolean registrationDone) {
}
@Override
public void preDeregister() throws Exception {
}
@Override
public void postDeregister() {
}
//=================================================================
// NOTIFICATION INFO
//=================================================================
public static final String NOTIFY_INIT = "INIT FAILED";
public static final String NOTIFY_CONNECT = "CONNECTION FAILED";
public static final String NOTIFY_ABANDON = "CONNECTION ABANDONED";
public static final String SLOW_QUERY_NOTIFICATION = "SLOW QUERY";
public static final String FAILED_QUERY_NOTIFICATION = "FAILED QUERY";
public static final String SUSPECT_ABANDONED_NOTIFICATION = "SUSPECT CONNECTION ABANDONED";
public static final String POOL_EMPTY = "POOL EMPTY";
public static final String SUSPECT_RETURNED_NOTIFICATION = "SUSPECT CONNECTION RETURNED";
@Override
public MBeanNotificationInfo[] getNotificationInfo() {
MBeanNotificationInfo[] pres = super.getNotificationInfo();
MBeanNotificationInfo[] loc = getDefaultNotificationInfo();
MBeanNotificationInfo[] aug = new MBeanNotificationInfo[pres.length + loc.length];
if (pres.length>0) System.arraycopy(pres, 0, aug, 0, pres.length);
if (loc.length >0) System.arraycopy(loc, 0, aug, pres.length, loc.length);
return aug;
}
public static MBeanNotificationInfo[] getDefaultNotificationInfo() {
String[] types = new String[] {NOTIFY_INIT, NOTIFY_CONNECT, NOTIFY_ABANDON, SLOW_QUERY_NOTIFICATION,
FAILED_QUERY_NOTIFICATION, SUSPECT_ABANDONED_NOTIFICATION, POOL_EMPTY, SUSPECT_RETURNED_NOTIFICATION};
String name = Notification.class.getName();
String description = "A connection pool error condition was met.";
MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description);
return new MBeanNotificationInfo[] {info};
}
/**
* Return true if the notification was sent successfully, false otherwise.
* @param type The notification type
* @param message The message
* @return true if the notification succeeded
*/
public boolean notify(final String type, String message) {
try {
Notification n = new Notification(
type,
this,
sequence.incrementAndGet(),
System.currentTimeMillis(),
"["+type+"] "+message);
sendNotification(n);
for (NotificationListener listener : listeners) {
listener.handleNotification(n,this);
}
return true;
}catch (Exception x) {
if (log.isDebugEnabled()) {
log.debug("Notify failed. Type="+type+"; Message="+message,x);
}
return false;
}
}
public void addListener(NotificationListener list) {
listeners.add(list);
}
public boolean removeListener(NotificationListener list) {
return listeners.remove(list);
}
//=================================================================
// POOL STATS
//=================================================================
@Override
public int getSize() {
return pool.getSize();
}
@Override
public int getIdle() {
return pool.getIdle();
}
@Override
public int getActive() {
return pool.getActive();
}
@Override
public int getNumIdle() {
return getIdle();
}
@Override
public int getNumActive() {
return getActive();
}
@Override
public int getWaitCount() {
return pool.getWaitCount();
}
@Override
public long getBorrowedCount() {
return pool.getBorrowedCount();
}
@Override
public long getReturnedCount() {
return pool.getReturnedCount();
}
@Override
public long getCreatedCount() {
return pool.getCreatedCount();
}
@Override
public long getReleasedCount() {
return pool.getReleasedCount();
}
@Override
public long getReconnectedCount() {
return pool.getReconnectedCount();
}
@Override
public long getRemoveAbandonedCount() {
return pool.getRemoveAbandonedCount();
}
@Override
public long getReleasedIdleCount() {
return pool.getReleasedIdleCount();
}
//=================================================================
// POOL OPERATIONS
//=================================================================
@Override
public void checkIdle() {
pool.checkIdle();
}
@Override
public void checkAbandoned() {
pool.checkAbandoned();
}
@Override
public void testIdle() {
pool.testAllIdle();
}
@Override
public void resetStats() {
pool.resetStats();
}
//=================================================================
// POOL PROPERTIES
//=================================================================
//=========================================================
// PROPERTIES / CONFIGURATION
//=========================================================
@Override
public String getConnectionProperties() {
return getPoolProperties().getConnectionProperties();
}
@Override
public Properties getDbProperties() {
return PoolUtilities.cloneWithoutPassword(getPoolProperties().getDbProperties());
}
@Override
public String getDefaultCatalog() {
return getPoolProperties().getDefaultCatalog();
}
@Override
public int getDefaultTransactionIsolation() {
return getPoolProperties().getDefaultTransactionIsolation();
}
@Override
public String getDriverClassName() {
return getPoolProperties().getDriverClassName();
}
@Override
public int getInitialSize() {
return getPoolProperties().getInitialSize();
}
@Override
public String getInitSQL() {
return getPoolProperties().getInitSQL();
}
@Override
public String getJdbcInterceptors() {
return getPoolProperties().getJdbcInterceptors();
}
@Override
public int getMaxActive() {
return getPoolProperties().getMaxActive();
}
@Override
public int getMaxIdle() {
return getPoolProperties().getMaxIdle();
}
@Override
public int getMaxWait() {
return getPoolProperties().getMaxWait();
}
@Override
public int getMinEvictableIdleTimeMillis() {
return getPoolProperties().getMinEvictableIdleTimeMillis();
}
@Override
public int getMinIdle() {
return getPoolProperties().getMinIdle();
}
@Override
public long getMaxAge() {
return getPoolProperties().getMaxAge();
}
@Override
public String getName() {
return this.getPoolName();
}
@Override
public int getNumTestsPerEvictionRun() {
return getPoolProperties().getNumTestsPerEvictionRun();
}
/**
* @return DOES NOT RETURN THE PASSWORD, IT WOULD SHOW UP IN JMX
*/
@Override
public String getPassword() {
return "Password not available as DataSource/JMX operation.";
}
@Override
public int getRemoveAbandonedTimeout() {
return getPoolProperties().getRemoveAbandonedTimeout();
}
@Override
public int getTimeBetweenEvictionRunsMillis() {
return getPoolProperties().getTimeBetweenEvictionRunsMillis();
}
@Override
public String getUrl() {
return getPoolProperties().getUrl();
}
@Override
public String getUsername() {
return getPoolProperties().getUsername();
}
@Override
public long getValidationInterval() {
return getPoolProperties().getValidationInterval();
}
@Override
public String getValidationQuery() {
return getPoolProperties().getValidationQuery();
}
@Override
public int getValidationQueryTimeout() {
return getPoolProperties().getValidationQueryTimeout();
}
/**
* {@inheritDoc}
*/
@Override
public String getValidatorClassName() {
return getPoolProperties().getValidatorClassName();
}
/**
* {@inheritDoc}
*/
@Override
public Validator getValidator() {
return getPoolProperties().getValidator();
}
@Override
public boolean isAccessToUnderlyingConnectionAllowed() {
return getPoolProperties().isAccessToUnderlyingConnectionAllowed();
}
@Override
public Boolean isDefaultAutoCommit() {
return getPoolProperties().isDefaultAutoCommit();
}
@Override
public Boolean isDefaultReadOnly() {
return getPoolProperties().isDefaultReadOnly();
}
@Override
public boolean isLogAbandoned() {
return getPoolProperties().isLogAbandoned();
}
@Override
public boolean isPoolSweeperEnabled() {
return getPoolProperties().isPoolSweeperEnabled();
}
@Override
public boolean isRemoveAbandoned() {
return getPoolProperties().isRemoveAbandoned();
}
@Override
public int getAbandonWhenPercentageFull() {
return getPoolProperties().getAbandonWhenPercentageFull();
}
@Override
public boolean isTestOnBorrow() {
return getPoolProperties().isTestOnBorrow();
}
@Override
public boolean isTestOnConnect() {
return getPoolProperties().isTestOnConnect();
}
@Override
public boolean isTestOnReturn() {
return getPoolProperties().isTestOnReturn();
}
@Override
public boolean isTestWhileIdle() {
return getPoolProperties().isTestWhileIdle();
}
@Override
public Boolean getDefaultAutoCommit() {
return getPoolProperties().getDefaultAutoCommit();
}
@Override
public Boolean getDefaultReadOnly() {
return getPoolProperties().getDefaultReadOnly();
}
@Override
public InterceptorDefinition[] getJdbcInterceptorsAsArray() {
return getPoolProperties().getJdbcInterceptorsAsArray();
}
@Override
public boolean getUseLock() {
return getPoolProperties().getUseLock();
}
@Override
public boolean isFairQueue() {
return getPoolProperties().isFairQueue();
}
@Override
public boolean isJmxEnabled() {
return getPoolProperties().isJmxEnabled();
}
@Override
public boolean isUseEquals() {
return getPoolProperties().isUseEquals();
}
@Override
public void setAbandonWhenPercentageFull(int percentage) {
getPoolProperties().setAbandonWhenPercentageFull(percentage);
}
@Override
public void setAccessToUnderlyingConnectionAllowed(boolean accessToUnderlyingConnectionAllowed) {
getPoolProperties().setAccessToUnderlyingConnectionAllowed(accessToUnderlyingConnectionAllowed);
}
@Override
public void setDbProperties(Properties dbProperties) {
getPoolProperties().setDbProperties(dbProperties);
}
@Override
public void setDefaultReadOnly(Boolean defaultReadOnly) {
getPoolProperties().setDefaultReadOnly(defaultReadOnly);
}
@Override
public void setMaxAge(long maxAge) {
getPoolProperties().setMaxAge(maxAge);
}
@Override
public void setName(String name) {
getPoolProperties().setName(name);
}
@Override
public String getPoolName() {
return getPoolProperties().getName();
}
@Override
public void setConnectionProperties(String connectionProperties) {
getPoolProperties().setConnectionProperties(connectionProperties);
}
@Override
public void setDefaultAutoCommit(Boolean defaultAutoCommit) {
getPoolProperties().setDefaultAutoCommit(defaultAutoCommit);
}
@Override
public void setDefaultCatalog(String defaultCatalog) {
getPoolProperties().setDefaultCatalog(defaultCatalog);
}
@Override
public void setDefaultTransactionIsolation(int defaultTransactionIsolation) {
getPoolProperties().setDefaultTransactionIsolation(defaultTransactionIsolation);
}
@Override
public void setDriverClassName(String driverClassName) {
getPoolProperties().setDriverClassName(driverClassName);
}
@Override
public void setFairQueue(boolean fairQueue) {
// noop - this pool is already running
throw new UnsupportedOperationException();
}
@Override
public void setInitialSize(int initialSize) {
// noop - this pool is already running
throw new UnsupportedOperationException();
}
@Override
public void setInitSQL(String initSQL) {
getPoolProperties().setInitSQL(initSQL);
}
@Override
public void setJdbcInterceptors(String jdbcInterceptors) {
// noop - this pool is already running
throw new UnsupportedOperationException();
}
@Override
public void setJmxEnabled(boolean jmxEnabled) {
// noop - this pool is already running and obviously jmx enabled
throw new UnsupportedOperationException();
}
@Override
public void setLogAbandoned(boolean logAbandoned) {
getPoolProperties().setLogAbandoned(logAbandoned);
}
@Override
public void setMaxActive(int maxActive) {
getPoolProperties().setMaxActive(maxActive);
//make sure the pool is properly configured
pool.checkPoolConfiguration(getPoolProperties());
}
@Override
public void setMaxIdle(int maxIdle) {
getPoolProperties().setMaxIdle(maxIdle);
//make sure the pool is properly configured
pool.checkPoolConfiguration(getPoolProperties());
}
@Override
public void setMaxWait(int maxWait) {
getPoolProperties().setMaxWait(maxWait);
}
@Override
public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
boolean shouldBeEnabled = getPoolProperties().isPoolSweeperEnabled();
//make sure pool cleaner starts/stops when it should
if (!wasEnabled && shouldBeEnabled) pool.initializePoolCleaner(getPoolProperties());
else if (wasEnabled && !shouldBeEnabled) pool.terminatePoolCleaner();
}
@Override
public void setMinIdle(int minIdle) {
getPoolProperties().setMinIdle(minIdle);
//make sure the pool is properly configured
pool.checkPoolConfiguration(getPoolProperties());
}
@Override
public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
getPoolProperties().setNumTestsPerEvictionRun(numTestsPerEvictionRun);
}
@Override
public void setPassword(String password) {
getPoolProperties().setPassword(password);
}
@Override
public void setRemoveAbandoned(boolean removeAbandoned) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setRemoveAbandoned(removeAbandoned);
boolean shouldBeEnabled = getPoolProperties().isPoolSweeperEnabled();
//make sure pool cleaner starts/stops when it should
if (!wasEnabled && shouldBeEnabled) pool.initializePoolCleaner(getPoolProperties());
else if (wasEnabled && !shouldBeEnabled) pool.terminatePoolCleaner();
}
@Override
public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setRemoveAbandonedTimeout(removeAbandonedTimeout);
boolean shouldBeEnabled = getPoolProperties().isPoolSweeperEnabled();
//make sure pool cleaner starts/stops when it should
if (!wasEnabled && shouldBeEnabled) pool.initializePoolCleaner(getPoolProperties());
else if (wasEnabled && !shouldBeEnabled) pool.terminatePoolCleaner();
}
@Override
public void setTestOnBorrow(boolean testOnBorrow) {
getPoolProperties().setTestOnBorrow(testOnBorrow);
}
@Override
public void setTestOnConnect(boolean testOnConnect) {
getPoolProperties().setTestOnConnect(testOnConnect);
}
@Override
public void setTestOnReturn(boolean testOnReturn) {
getPoolProperties().setTestOnReturn(testOnReturn);
}
@Override
public void setTestWhileIdle(boolean testWhileIdle) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setTestWhileIdle(testWhileIdle);
boolean shouldBeEnabled = getPoolProperties().isPoolSweeperEnabled();
//make sure pool cleaner starts/stops when it should
if (!wasEnabled && shouldBeEnabled) pool.initializePoolCleaner(getPoolProperties());
else if (wasEnabled && !shouldBeEnabled) pool.terminatePoolCleaner();
}
@Override
public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
boolean shouldBeEnabled = getPoolProperties().isPoolSweeperEnabled();
//make sure pool cleaner starts/stops when it should
if (!wasEnabled && shouldBeEnabled) {
pool.initializePoolCleaner(getPoolProperties());
} else if (wasEnabled) {
pool.terminatePoolCleaner();
if (shouldBeEnabled) {
pool.initializePoolCleaner(getPoolProperties());
}
}
}
@Override
public void setUrl(String url) {
getPoolProperties().setUrl(url);
}
@Override
public void setUseEquals(boolean useEquals) {
getPoolProperties().setUseEquals(useEquals);
}
@Override
public void setUseLock(boolean useLock) {
getPoolProperties().setUseLock(useLock);
}
@Override
public void setUsername(String username) {
getPoolProperties().setUsername(username);
}
@Override
public void setValidationInterval(long validationInterval) {
getPoolProperties().setValidationInterval(validationInterval);
}
@Override
public void setValidationQuery(String validationQuery) {
getPoolProperties().setValidationQuery(validationQuery);
}
@Override
public void setValidationQueryTimeout(int validationQueryTimeout) {
getPoolProperties().setValidationQueryTimeout(validationQueryTimeout);
}
/**
* {@inheritDoc}
*/
@Override
public void setValidatorClassName(String className) {
getPoolProperties().setValidatorClassName(className);
}
/**
* {@inheritDoc}
*/
@Override
public int getSuspectTimeout() {
return getPoolProperties().getSuspectTimeout();
}
/**
* {@inheritDoc}
*/
@Override
public void setSuspectTimeout(int seconds) {
getPoolProperties().setSuspectTimeout(seconds);
}
/**
* {@inheritDoc}
*/
@Override
public void setDataSource(Object ds) {
getPoolProperties().setDataSource(ds);
}
/**
* {@inheritDoc}
*/
@Override
public Object getDataSource() {
return getPoolProperties().getDataSource();
}
/**
* {@inheritDoc}
*/
@Override
public void setDataSourceJNDI(String jndiDS) {
getPoolProperties().setDataSourceJNDI(jndiDS);
}
/**
* {@inheritDoc}
*/
@Override
public String getDataSourceJNDI() {
return getPoolProperties().getDataSourceJNDI();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isAlternateUsernameAllowed() {
return getPoolProperties().isAlternateUsernameAllowed();
}
/**
* {@inheritDoc}
*/
@Override
public void setAlternateUsernameAllowed(boolean alternateUsernameAllowed) {
getPoolProperties().setAlternateUsernameAllowed(alternateUsernameAllowed);
}
/**
* {@inheritDoc}
*/
@Override
public void setValidator(Validator validator) {
getPoolProperties().setValidator(validator);
}
/**
* {@inheritDoc}
*/
@Override
public void setCommitOnReturn(boolean commitOnReturn) {
getPoolProperties().setCommitOnReturn(commitOnReturn);
}
/**
* {@inheritDoc}
*/
@Override
public boolean getCommitOnReturn() {
return getPoolProperties().getCommitOnReturn();
}
/**
* {@inheritDoc}
*/
@Override
public void setRollbackOnReturn(boolean rollbackOnReturn) {
getPoolProperties().setRollbackOnReturn(rollbackOnReturn);
}
/**
* {@inheritDoc}
*/
@Override
public boolean getRollbackOnReturn() {
return getPoolProperties().getRollbackOnReturn();
}
/**
* {@inheritDoc}
*/
@Override
public void setUseDisposableConnectionFacade(boolean useDisposableConnectionFacade) {
getPoolProperties().setUseDisposableConnectionFacade(useDisposableConnectionFacade);
}
/**
* {@inheritDoc}
*/
@Override
public boolean getUseDisposableConnectionFacade() {
return getPoolProperties().getUseDisposableConnectionFacade();
}
/**
* {@inheritDoc}
*/
@Override
public void setLogValidationErrors(boolean logValidationErrors) {
getPoolProperties().setLogValidationErrors(logValidationErrors);
}
/**
* {@inheritDoc}
*/
@Override
public boolean getLogValidationErrors() {
return getPoolProperties().getLogValidationErrors();
}
/**
* {@inheritDoc}
*/
@Override
public boolean getPropagateInterruptState() {
return getPoolProperties().getPropagateInterruptState();
}
/**
* {@inheritDoc}
*/
@Override
public void setPropagateInterruptState(boolean propagateInterruptState) {
getPoolProperties().setPropagateInterruptState(propagateInterruptState);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isIgnoreExceptionOnPreLoad() {
return getPoolProperties().isIgnoreExceptionOnPreLoad();
}
/**
* {@inheritDoc}
*/
@Override
public void setIgnoreExceptionOnPreLoad(boolean ignoreExceptionOnPreLoad) {
// noop - this pool is already running
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public boolean getUseStatementFacade() {
return getPoolProperties().getUseStatementFacade();
}
/**
* {@inheritDoc}
*/
@Override
public void setUseStatementFacade(boolean useStatementFacade) {
getPoolProperties().setUseStatementFacade(useStatementFacade);
}
/**
* {@inheritDoc}
*/
@Override
public void purge() {
pool.purge();
}
/**
* {@inheritDoc}
*/
@Override
public void purgeOnReturn() {
pool.purgeOnReturn();
}
}

View File

@@ -0,0 +1,87 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.jmx;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
public interface ConnectionPoolMBean extends PoolConfiguration {
//=================================================================
// POOL STATS
//=================================================================
public int getSize();
public int getIdle();
public int getActive();
public int getNumIdle();
public int getNumActive();
public int getWaitCount();
public long getBorrowedCount();
public long getReturnedCount();
public long getCreatedCount();
public long getReleasedCount();
public long getReconnectedCount();
public long getRemoveAbandonedCount();
public long getReleasedIdleCount();
//=================================================================
// POOL OPERATIONS
//=================================================================
public void checkIdle();
public void checkAbandoned();
public void testIdle();
/**
* Purges all connections in the pool.
* For connections currently in use, these connections will be
* purged when returned on the pool. This call also
* purges connections that are idle and in the pool
* To only purge used/active connections see {@link #purgeOnReturn()}
*/
public void purge();
/**
* Purges connections when they are returned from the pool.
* This call does not purge idle connections until they are used.
* To purge idle connections see {@link #purge()}
*/
public void purgeOnReturn();
/**
* reset the statistics of this pool.
*/
public void resetStats();
//=================================================================
// POOL NOTIFICATIONS
//=================================================================
}

View File

@@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.jmx;
import java.lang.management.ManagementFactory;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
public class JmxUtil {
private static final Log log = LogFactory.getLog(JmxUtil.class);
public static ObjectName registerJmx(ObjectName base, String keyprop, Object obj) {
ObjectName oname = null;
try {
oname = getObjectName(base, keyprop);
if (oname != null) ManagementFactory.getPlatformMBeanServer().registerMBean(obj, oname);
} catch (Exception e) {
log.error("Jmx registration failed.",e);
}
return oname;
}
public static void unregisterJmx(ObjectName oname) {
if (oname ==null) return;
try {
ManagementFactory.getPlatformMBeanServer().unregisterMBean(oname);
} catch (Exception e) {
log.error("Jmx unregistration failed.",e);
}
}
private static ObjectName getObjectName(ObjectName base, String keyprop)
throws MalformedObjectNameException {
if (base == null) return null;
StringBuilder OnameStr = new StringBuilder(base.toString());
if (keyprop != null) OnameStr.append(keyprop);
ObjectName oname = new ObjectName(OnameStr.toString());
return oname;
}
}

View File

@@ -0,0 +1,406 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<mbeans-descriptors>
<mbean name="TomcatJDBCPool"
description="Provides per diagnostic metrics and notifications for JDBC operations"
domain="tomcat"
group="jdbc"
type="org.apache.tomcat.jdbc.pool.DataSource">
<attribute name="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute name="size"
description="The number of established connections in the pool, idle and in use"
type="java.lang.Integer"
writeable="false"/>
<attribute name="idle"
description="The number of established connections in the pool that are idle"
type="java.lang.Integer"
writeable="false"/>
<attribute name="numIdle"
description="Same as the idle attribute"
type="java.lang.Integer"
writeable="false"/>
<attribute name="active"
description="The number of established connections in the pool that are in use"
type="java.lang.Integer"
writeable="false"/>
<attribute name="numActive"
description="Same as the active attribute"
type="java.lang.Integer"
writeable="false"/>
<attribute name="poolSweeperEnabled"
description="Returns true if the pool has a background thread running"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="url"
description="The JDBC url for this connection pool"
type="java.lang.String"
writeable="false"/>
<attribute name="driverClassName"
description="The JDBC driver class for this connection pool"
type="java.lang.String"
writeable="false"/>
<attribute name="defaultAutoCommit"
description="The JDBC auto commit setting for new connections"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="defaultReadOnly"
description="The JDBC read only setting for new connections"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="defaultTransactionIsolation"
description="The JDBC transaction isolation setting for new connections"
type="java.lang.Integer"
writeable="false"/>
<attribute name="connectionProperties"
description="The connection properties that will be set for new connections. Format of the string will be [propertyName=property;]*"
type="java.lang.String"
writeable="false"/>
<attribute name="defaultCatalog"
description="The JDBC transaction isolation setting for new connections"
type="java.lang.String"
writeable="false"/>
<attribute name="initialSize"
description="The number of connections opened at pool startup"
type="java.lang.Integer"
writeable="false"/>
<attribute name="maxActive"
description="The maximum number of open connections"
type="java.lang.Integer"
writeable="false"/>
<attribute name="maxIdle"
description="The max number of idle connections"
type="java.lang.Integer"
writeable="false"/>
<attribute name="minIdle"
description="The minimum number of open connections"
type="java.lang.Integer"
writeable="false"/>
<attribute name="maxWait"
description="The time to wait in milliseconds before an SQLException is thrown when a connection is requested"
type="java.lang.Integer"
writeable="false"/>
<attribute name="validationQuery"
description="The query to run during validation"
type="java.lang.String"
writeable="false"/>
<attribute name="validationQueryTimeout"
description="The timeout in seconds before a connection validation queries fail"
type="java.lang.Integer"
writeable="false" />
<attribute name="testOnBorrow"
description="True if validation happens when a connection is requested"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="testOnReturn"
description="True if validation happens when a connection is returned"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="testWhileIdle"
description="True if validation happens when a connection is not in use (idle)"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="timeBetweenEvictionRunsMillis"
description="Sleep time for background thread in between pool checks"
type="java.lang.Integer"
writeable="false"/>
<attribute name="numTestsPerEvictionRun"
description="Not in use"
type="java.lang.Integer"
writeable="false"/>
<attribute name="minEvictableIdleTimeMillis"
description="Minimum amount of time a connection stays idle before it is evicted"
type="java.lang.Integer"
writeable="false"/>
<attribute name="accessToUnderlyingConnectionAllowed"
description="Returns true if one can retrieve the actual JDBC connection"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="removeAbandoned"
description="Returns true if connection in use can be timed out"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="removeAbandonedTimeout"
description="Timeout in seconds for connections in use"
type="java.lang.Integer"
writeable="false"/>
<attribute name="logAbandoned"
description="If true, stack trace will be recorded and printed out for timed out connection"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="loginTimeout"
description="Not in use"
type="java.lang.Integer"
writeable="false"/>
<attribute name="name"
description="The name of the connection pool, will be used in the ObjectName of the actual pool"
type="java.lang.String"
writeable="false"/>
<attribute name="password"
description="For security purposes,this doesn't return anything"
type="java.lang.String"
writeable="false"/>
<attribute name="username"
description="The username used to open connections"
type="java.lang.String"
writeable="false"/>
<attribute name="validationInterval"
description="If larger than zero than validation will only occur after the interval milliseconds has passed"
type="java.lang.Long"
writeable="false"/>
<attribute name="initSQL"
description="An SQL executed once per connection, when it is established"
type="java.lang.String"
writeable="false"/>
<attribute name="testOnConnect"
description="Validate connection after connection has been established"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="jdbcInterceptors"
description="The interceptors configured for this pool"
type="java.lang.String"
writeable="false"/>
<attribute name="jmxEnabled"
description="Register the pool with JMX or not"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="fairQueue"
description="a fair queue is being used by the connection pool"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="abandonWhenPercentageFull"
description="Connections that have been abandoned isn't closed unless connections in use are above this percentage"
type="java.lang.Integer"
writeable="false"/>
<attribute name="maxAge"
description="Time in milliseconds to keep this connection alive even when used"
type="java.lang.Long"
writeable="false"/>
<attribute name="useEquals"
description="Set to true if you wish the ProxyConnection class to use String.equals and set to false when you wish to use == when comparing method names"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="useLock"
description="If true, use a lock when operations are performed on the connection object"
type="java.lang.Boolean"
is="false"
writeable="false"/>
<attribute name="suspectTimeout"
description="Timeout in seconds for connection that suspected to have been abandoned"
type="java.lang.Integer"
writeable="false"/>
<attribute name="rollbackOnReturn"
description="If autoCommit==false then the pool can terminate the transaction by calling rollback on the connection as it is returned to the pool"
type="java.lang.Boolean"
is="false"
writeable="false"/>
<attribute name="commitOnReturn"
description="If autoCommit==false then the pool can complete the transaction by calling commit on the connection as it is returned to the pool"
type="java.lang.Boolean"
is="false"
writeable="false"/>
<attribute name="alternateUsernameAllowed"
description="If true, getConnection(username,password) is allowed"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="dataSource"
description="Data source that is injected into the pool"
type="javax.sql.DataSource"
writeable="false"/>
<attribute name="dataSourceJNDI"
description="The JNDI name for a data source to be looked up"
type="java.lang.String"
writeable="false"/>
<attribute name="useDisposableConnectionFacade"
description="If true, connection pool is configured to use a connection facade to prevent re-use of connection after close() has been invoked"
type="java.lang.Boolean"
is="false"
writeable="false"/>
<attribute name="logValidationErrors"
description="Log errors during the validation phase to the log file"
type="java.lang.Boolean"
is="false"
writeable="false"/>
<attribute name="validatorClassName"
description="The name of validator class which implements org.apache.tomcat.jdbc.pool.Validator interface"
type="java.lang.String"
writeable="false"/>
<attribute name="waitCount"
description="The number of threads waiting for a connection"
type="java.lang.Integer"
writeable="false"/>
<attribute name="propagateInterruptState"
description="If true, propagate the interrupt state for a thread that has been interrupted"
type="java.lang.Boolean"
is="false"
writeable="false"/>
<attribute name="ignoreExceptionOnPreLoad"
description="If true, ignore error of connection creation while initializing the pool"
type="java.lang.Boolean"
is="true"
writeable="false"/>
<attribute name="useStatementFacade"
description="If true, connection pool is configured to wrap statements."
type="java.lang.Boolean"
is="false"
writeable="false"/>
<attribute name="borrowedCount"
description="The total number of connections borrowed from this pool"
type="java.lang.Long"
writeable="false"/>
<attribute name="createdCount"
description="The total number of connections created by this pool"
type="java.lang.Long"
writeable="false"/>
<attribute name="returnedCount"
description="The total number of connections returned to this pool"
type="java.lang.Long"
writeable="false"/>
<attribute name="releasedCount"
description="The total number of connections released from this pool"
type="java.lang.Long"
writeable="false"/>
<attribute name="reconnectedCount"
description="The total number of connections reconnected by this pool."
type="java.lang.Long"
writeable="false"/>
<attribute name="removeAbandonedCount"
description="The total number of connections released by remove abandoned."
type="java.lang.Long"
writeable="false"/>
<attribute name="releasedIdleCount"
description="The total number of connections released by eviction."
type="java.lang.Long"
writeable="false"/>
<operation name="checkIdle"
description="forces a check of idle connections"
impact="ACTION"
returnType="void" />
<operation name="checkAbandoned"
description="forces a check of abandoned connections"
impact="ACTION"
returnType="void" />
<operation name="testIdle"
description="forces a validation of abandoned connections"
impact="ACTION"
returnType="void" />
<operation name="purge"
description="Purges all connections in the pool"
impact="ACTION"
returnType="void" />
<operation name="purgeOnReturn"
description="Purges connections when they are returned from the pool"
impact="ACTION"
returnType="void" />
<operation name="resetStats"
description="reset the statistics of this pool."
impact="ACTION"
returnType="void" />
</mbean>
</mbeans-descriptors>

View File

@@ -0,0 +1,105 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.bugs;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.test.DefaultProperties;
public class Bug51582 {
public static void main(String[] args) throws SQLException {
org.apache.tomcat.jdbc.pool.DataSource datasource = null;
PoolConfiguration p = new DefaultProperties();
p.setJmxEnabled(true);
p.setTestOnBorrow(false);
p.setTestOnReturn(false);
p.setValidationInterval(1000);
p.setTimeBetweenEvictionRunsMillis(2000);
p.setMaxWait(2000);
p.setMinEvictableIdleTimeMillis(1000);
datasource = new org.apache.tomcat.jdbc.pool.DataSource();
datasource.setPoolProperties(p);
datasource.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx(threshold=200)");
ConnectionPool pool = datasource.createPool();
Connection con = pool.getConnection();
Statement st = con.createStatement();
try {
st.execute("DROP ALIAS SLEEP");
} catch (Exception ignore) {
// Ignore
}
st.execute("CREATE ALIAS SLEEP AS $$\nboolean sleep() {\n try {\n Thread.sleep(10000);\n return true; } catch (Exception x) {\n return false;\n }\n}\n$$;");
st.close();
con.close();
int iter = 0;
while ((iter++) < 10) {
final Connection connection = pool.getConnection();
final CallableStatement s = connection.prepareCall("{CALL SLEEP()}");
List<Thread> threadList = new ArrayList<>();
for (int l = 0; l < 3; l++) {
final int i = l;
Thread thread = new Thread() {
@Override
public void run() {
try {
if (i == 0) {
Thread.sleep(1000);
s.cancel();
} else if (i == 1) {
// or use some other statement which will block
// for a longer time
long start = System.currentTimeMillis();
System.out.println("[" + getName() +
"] Calling SP SLEEP");
s.execute();
System.out.println("[" + getName() +
"] Executed SP SLEEP [" +
(System.currentTimeMillis() - start) +
"]");
} else {
Thread.sleep(1000);
connection.close();
}
} catch (InterruptedException e) {
} catch (SQLException e) {
e.printStackTrace();
}
}
};
threadList.add(thread);
thread.start();
}
for (Thread t : threadList) {
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}

View File

@@ -0,0 +1,177 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.bugs;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolExhaustedException;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.apache.tomcat.jdbc.test.DefaultProperties;
@RunWith(Parameterized.class)
public class Bug53367 {
private boolean fairQueue;
public Bug53367(boolean fair) {
this.fairQueue = fair;
}
@Parameterized.Parameters
public static Collection<Object[]> parameters() {
return Arrays.asList(new Object[][]{
new Object[] {Boolean.TRUE},
new Object[] {Boolean.FALSE},
});
}
@Test
public void testPool() throws SQLException, InterruptedException {
DriverManager.setLoginTimeout(1);
PoolProperties poolProperties = new DefaultProperties();
int threadsCount = 3;
poolProperties.setMaxActive(threadsCount);
poolProperties.setMaxIdle(threadsCount);
poolProperties.setMinIdle(0);
poolProperties.setMaxWait(5000);
poolProperties.setInitialSize(0);
poolProperties.setRemoveAbandoned(true);
poolProperties.setRemoveAbandonedTimeout(300);
poolProperties.setRollbackOnReturn(true);
poolProperties.setFairQueue(fairQueue);
final DataSource ds = new DataSource(poolProperties);
final CountDownLatch openedLatch = new CountDownLatch(threadsCount);
final CountDownLatch closedLatch = new CountDownLatch(threadsCount);
final CountDownLatch toCloseLatch = new CountDownLatch(1);
for (int i = 0; i < threadsCount; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Connection connection = ds.getConnection();
openedLatch.countDown();
toCloseLatch.await();
connection.close();
closedLatch.countDown();
} catch (Exception e) {
System.err.println("Step 1:"+e.getMessage());
}
}
}).start();
}
openedLatch.await();
ConnectionPool pool = ds.getPool();
//Now we have 3 initialized busy connections
Assert.assertEquals(0, pool.getIdle());
Assert.assertEquals(threadsCount, pool.getActive());
Assert.assertEquals(threadsCount, pool.getSize());
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < threadsCount; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// Expected to fail
try (Connection c = ds.getConnection()) {
} catch (Exception e) {
System.err.println("Step 2:"+e.getMessage());
}
}
});
thread.start();
threads.add(thread);
}
for (Thread thread : threads) {
thread.interrupt();
}
for (Thread thread : threads) {
thread.join();
}
//Still 3 active connections
Assert.assertEquals(0, pool.getIdle());
Assert.assertEquals(threadsCount, pool.getActive());
Assert.assertEquals(threadsCount, pool.getSize());
toCloseLatch.countDown();
closedLatch.await();
//Here comes the bug! No more active connections and unable to establish new connections.
Assert.assertEquals(threadsCount, pool.getIdle()); // <-- Should be threadsCount (3) here
Assert.assertEquals(0, pool.getActive());
Assert.assertEquals(threadsCount, pool.getSize());
final AtomicInteger failedCount = new AtomicInteger();
final ArrayBlockingQueue<Connection> cons = new ArrayBlockingQueue<>(threadsCount);
threads.clear();
for (int i = 0; i < threadsCount; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
cons.add(ds.getConnection());
} catch (PoolExhaustedException e) {
failedCount.incrementAndGet();
System.err.println("Step 3:"+e.getMessage());
} catch (Exception e) {
System.err.println("Step 4:"+e.getMessage());
throw new RuntimeException(e);
}
}
});
thread.start();
threads.add(thread);
}
for (Thread thread : threads) {
thread.join();
}
Assert.assertEquals(0, failedCount.get());
Assert.assertEquals(0, pool.getIdle());
Assert.assertEquals(threadsCount, pool.getActive());
Assert.assertEquals(threadsCount, pool.getSize());
for (Connection con : cons) {
con.close();
}
Assert.assertEquals(threadsCount, pool.getIdle());
Assert.assertEquals(0, pool.getActive());
Assert.assertEquals(threadsCount, pool.getSize());
}
}

View File

@@ -0,0 +1,64 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.bugs;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.apache.tomcat.jdbc.test.DefaultProperties;
@RunWith(Parameterized.class)
public class Bug54225 {
private String initSQL;
public Bug54225(String initSQL) {
this.initSQL = initSQL;
}
@Parameterized.Parameters
public static Collection<Object[]> parameters() {
return Arrays.asList(new Object[][]{
new Object[] {""},
new Object[] {null},
});
}
@Test
public void testPool() throws SQLException {
PoolProperties poolProperties = new DefaultProperties();
poolProperties.setMinIdle(0);
poolProperties.setInitialSize(0);
poolProperties.setMaxWait(5000);
poolProperties.setRemoveAbandoned(true);
poolProperties.setRemoveAbandonedTimeout(300);
poolProperties.setRollbackOnReturn(true);
poolProperties.setInitSQL(initSQL);
final DataSource ds = new DataSource(poolProperties);
ds.getConnection().close();
Assert.assertNull(poolProperties.getInitSQL());
}
}

View File

@@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.bugs;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.PooledConnection;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.apache.tomcat.jdbc.test.DefaultProperties;
public class Bug54227 {
public Bug54227() {
}
@Test
public void testPool() throws SQLException, InterruptedException {
PoolProperties poolProperties = new DefaultProperties();
poolProperties.setMinIdle(0);
poolProperties.setInitialSize(0);
poolProperties.setMaxActive(1);
poolProperties.setMaxWait(5000);
poolProperties.setMaxAge(100);
poolProperties.setRemoveAbandoned(false);
final DataSource ds = new DataSource(poolProperties);
Connection con;
Connection actual1;
Connection actual2;
con = ds.getConnection();
actual1 = ((PooledConnection)con).getConnection();
con.close();
con = ds.getConnection();
actual2 = ((PooledConnection)con).getConnection();
Assert.assertSame(actual1, actual2);
con.close();
Thread.sleep(150);
con = ds.getConnection();
actual2 = ((PooledConnection)con).getConnection();
Assert.assertNotSame(actual1, actual2);
con.close();
}
}

View File

@@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.bugs;
import java.sql.SQLException;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.apache.tomcat.jdbc.test.DefaultProperties;
public class Bug54978 {
@Test
public void testIllegalValidationQuery() {
PoolProperties poolProperties = new DefaultProperties();
poolProperties.setMinIdle(0);
poolProperties.setInitialSize(1);
poolProperties.setMaxActive(1);
poolProperties.setMaxWait(5000);
poolProperties.setMaxAge(100);
poolProperties.setRemoveAbandoned(false);
poolProperties.setTestOnBorrow(true);
poolProperties.setTestOnConnect(false);
poolProperties.setValidationQuery("sdadsada");
final DataSource ds = new DataSource(poolProperties);
try {
ds.getConnection().close();
Assert.fail("Validation should have failed.");
}catch (SQLException x) {
}
}
@Test
public void testIllegalValidationQueryWithLegalInit() throws SQLException {
PoolProperties poolProperties = new DefaultProperties();
poolProperties.setMinIdle(0);
poolProperties.setInitialSize(1);
poolProperties.setMaxActive(1);
poolProperties.setMaxWait(5000);
poolProperties.setMaxAge(100);
poolProperties.setRemoveAbandoned(false);
poolProperties.setTestOnBorrow(true);
poolProperties.setTestOnConnect(false);
poolProperties.setValidationQuery("sdadsada");
poolProperties.setInitSQL("SELECT 1");
final DataSource ds = new DataSource(poolProperties);
ds.getConnection().close();
}
}

View File

@@ -0,0 +1,140 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class ShouldForceReconnectTest {
private ConnectionPool pool;
private PoolProperties properties;
private static final String DEFAULT_USER = "username_def";
private static final String DEFAULT_PASSWD = "password_def";
private static final String ALT_USER = "username_alt";
private static final String ALT_PASSWD = "password_alt";
@Before
public void setUp() throws Exception {
properties = new PoolProperties();
properties.setUsername(DEFAULT_USER);
properties.setPassword(DEFAULT_PASSWD);
properties.setAlternateUsernameAllowed(true);
properties.setInitialSize(0);
properties.setRemoveAbandoned(false);
properties.setTimeBetweenEvictionRunsMillis(-1);
pool = new ConnectionPool(properties);
}
@After
public void tearDown() throws Exception {
}
@Test
public void testShouldForceReconnect() throws Exception {
PooledConnection con = new PooledConnection(properties, pool);
//connection previously connect with default
configureDefault(con);
Assert.assertFalse(con.shouldForceReconnect(null, null));
configureDefault(con);
Assert.assertFalse(con.shouldForceReconnect(DEFAULT_USER, DEFAULT_PASSWD));
configureDefault(con);
Assert.assertFalse(con.shouldForceReconnect(null,DEFAULT_PASSWD));
configureDefault(con);
Assert.assertFalse(con.shouldForceReconnect(DEFAULT_USER, null));
configureDefault(con);
Assert.assertTrue(con.shouldForceReconnect(ALT_USER,ALT_PASSWD));
configureDefault(con);
Assert.assertTrue(con.shouldForceReconnect(null,ALT_PASSWD));
configureDefault(con);
Assert.assertTrue(con.shouldForceReconnect(ALT_USER,null));
//connection previously connect with alternate
configureAlt(con);
Assert.assertFalse(con.shouldForceReconnect(ALT_USER, ALT_PASSWD));
configureAlt(con);
Assert.assertTrue(con.shouldForceReconnect(null, null));
configureAlt(con);
Assert.assertTrue(con.shouldForceReconnect(DEFAULT_USER, DEFAULT_PASSWD));
configureAlt(con);
Assert.assertTrue(con.shouldForceReconnect(null, DEFAULT_PASSWD));
configureAlt(con);
Assert.assertTrue(con.shouldForceReconnect(DEFAULT_USER, null));
configureAlt(con);
Assert.assertTrue(con.shouldForceReconnect(null,ALT_PASSWD));
configureAlt(con);
Assert.assertTrue(con.shouldForceReconnect(ALT_USER,null));
//test changes in username password
configureDefault(con);
Assert.assertFalse(con.shouldForceReconnect(null, null));
Assert.assertTrue(con.shouldForceReconnect(ALT_USER, ALT_PASSWD));
Assert.assertFalse(con.shouldForceReconnect(ALT_USER, ALT_PASSWD));
Assert.assertTrue(con.shouldForceReconnect(null, null));
configureDefault(con);
Assert.assertFalse(con.shouldForceReconnect(DEFAULT_USER, DEFAULT_PASSWD));
Assert.assertTrue(con.shouldForceReconnect(ALT_USER, ALT_PASSWD));
Assert.assertFalse(con.shouldForceReconnect(ALT_USER, ALT_PASSWD));
Assert.assertTrue(con.shouldForceReconnect(DEFAULT_USER, DEFAULT_PASSWD));
configureAlt(con);
Assert.assertFalse(con.shouldForceReconnect(ALT_USER, ALT_PASSWD));
Assert.assertFalse(con.shouldForceReconnect(ALT_USER, ALT_PASSWD));
Assert.assertTrue(con.shouldForceReconnect(DEFAULT_USER, DEFAULT_PASSWD));
Assert.assertFalse(con.shouldForceReconnect(null, null));
Assert.assertTrue(con.shouldForceReconnect(ALT_USER, ALT_PASSWD));
configureAlt(con);
Assert.assertTrue(con.shouldForceReconnect(DEFAULT_USER, DEFAULT_PASSWD));
Assert.assertTrue(con.shouldForceReconnect(ALT_USER, ALT_PASSWD));
Assert.assertFalse(con.shouldForceReconnect(ALT_USER, ALT_PASSWD));
Assert.assertTrue(con.shouldForceReconnect(DEFAULT_USER, DEFAULT_PASSWD));
}
private void configureAlt(PooledConnection con) {
con.getAttributes().put(PooledConnection.PROP_USER, ALT_USER);
con.getAttributes().put(PooledConnection.PROP_PASSWORD, ALT_PASSWD);
}
private void configureDefault(PooledConnection con) {
con.getAttributes().put(PooledConnection.PROP_USER, DEFAULT_USER);
con.getAttributes().put(PooledConnection.PROP_PASSWORD, DEFAULT_PASSWD);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.lang.reflect.Method;
import java.security.SecureRandom;
public class InduceSlowQuery extends AbstractQueryReport {
public static final SecureRandom random = new SecureRandom();
public InduceSlowQuery() {
// TODO Auto-generated constructor stub
}
public void doWait() {
try {
int b = random.nextInt(10);
if (b == 0) {
Thread.sleep(random.nextInt(2000));
}
} catch (InterruptedException x) {
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Object result = super.invoke(proxy, method, args);
return result;
}
@Override
protected void prepareCall(String query, long time) {
}
@Override
protected void prepareStatement(String sql, long time) {
}
@Override
public void closeInvoked() {
}
@Override
protected String reportQuery(String query, Object[] args, String name, long start, long delta) {
doWait();
return super.reportQuery(query, args, name, start, delta);
}
@Override
protected String reportSlowQuery(String query, Object[] args, String name, long start, long delta) {
doWait();
return super.reportSlowQuery(query, args, name, start, delta);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Statement;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Interceptor that counts opened Statements. Is used by tests.
*/
public class StatementCounterInterceptor extends StatementDecoratorInterceptor {
private final AtomicInteger countOpen = new AtomicInteger();
private final AtomicInteger countClosed = new AtomicInteger();
public int getActiveCount() {
return countOpen.get() - countClosed.get();
}
@Override
protected Object createDecorator(Object proxy, Method method,
Object[] args, Object statement, Constructor<?> constructor,
String sql) throws InstantiationException, IllegalAccessException,
InvocationTargetException {
Object result;
StatementProxy statementProxy = new StatementProxy(
(Statement) statement, sql);
result = constructor.newInstance(new Object[] { statementProxy });
statementProxy.setActualProxy(result);
statementProxy.setConnection(proxy);
statementProxy.setConstructor(constructor);
countOpen.incrementAndGet();
return result;
}
private class StatementProxy extends
StatementDecoratorInterceptor.StatementProxy<Statement> {
public StatementProxy(Statement delegate, String sql) {
super(delegate, sql);
}
@Override
public void closeInvoked() {
countClosed.incrementAndGet();
super.closeInvoked();
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.interceptor;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
import org.apache.tomcat.jdbc.pool.PooledConnection;
public class TestInterceptor extends JdbcInterceptor {
public static boolean poolstarted = false;
public static boolean poolclosed = false;
public static final AtomicInteger instancecount = new AtomicInteger(0);
@Override
public void poolClosed(ConnectionPool pool) {
super.poolClosed(pool);
poolclosed = true;
}
@Override
public void poolStarted(ConnectionPool pool) {
super.poolStarted(pool);
poolstarted = true;
}
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
// NO-OP
}
@Override
public void setProperties(Map<String, InterceptorProperty> properties) {
instancecount.incrementAndGet();
super.setProperties(properties);
}
}

View File

@@ -0,0 +1,117 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer;
public class AbandonPercentageTest extends DefaultTestCase {
@Test
public void testDefaultAbandon() throws Exception {
this.datasource.setMaxActive(100);
this.datasource.setMaxIdle(100);
this.datasource.setInitialSize(0);
this.datasource.getPoolProperties().setAbandonWhenPercentageFull(0);
this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(100);
this.datasource.getPoolProperties().setRemoveAbandoned(true);
this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1);
try (Connection con = datasource.getConnection()) {
Assert.assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
Thread.sleep(2000);
Assert.assertEquals("Number of connections active/busy should be 0",0,datasource.getPool().getActive());
}
}
@Test
public void testMaxedOutAbandon() throws Exception {
int size = 100;
this.datasource.setMaxActive(size);
this.datasource.setMaxIdle(size);
this.datasource.setInitialSize(0);
this.datasource.getPoolProperties().setAbandonWhenPercentageFull(100);
this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(100);
this.datasource.getPoolProperties().setRemoveAbandoned(true);
this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1);
try (Connection con = datasource.getConnection()) {
Assert.assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
Thread.sleep(2000);
Assert.assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
}
}
@Test
public void testResetConnection() throws Exception {
int size = 1;
this.datasource.setMaxActive(size);
this.datasource.setMaxIdle(size);
this.datasource.setInitialSize(0);
this.datasource.getPoolProperties().setAbandonWhenPercentageFull(100);
this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(100);
this.datasource.getPoolProperties().setRemoveAbandoned(true);
this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1);
this.datasource.getPoolProperties().setJdbcInterceptors(ResetAbandonedTimer.class.getName());
try (Connection con = datasource.getConnection()) {
Assert.assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
for (int i=0; i<20; i++) {
Thread.sleep(200);
// This call is here to ensure the pool thinks the connection
// is being used.
con.isClosed();
}
Assert.assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
}
}
@Test
public void testHalfway() throws Exception {
int size = 100;
this.datasource.setMaxActive(size);
this.datasource.setMaxIdle(size);
this.datasource.setInitialSize(0);
this.datasource.getPoolProperties().setAbandonWhenPercentageFull(50);
this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(500);
this.datasource.getPoolProperties().setRemoveAbandoned(true);
this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1);
Connection[] con = new Connection[size];
con[0] = datasource.getConnection();
Assert.assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
for (int i=1; i<25; i++) {
con[i] = datasource.getConnection();
}
Assert.assertEquals("Number of connections active/busy should be 25",25,datasource.getPool().getActive());
Thread.sleep(2500);
Assert.assertEquals("Number of connections active/busy should be 25",25,datasource.getPool().getActive());
this.datasource.getPoolProperties().setRemoveAbandonedTimeout(100);
for (int i=25; i<con.length; i++) {
con[i] = datasource.getConnection();
}
int active = datasource.getPool().getActive();
System.out.println("Active:"+active);
Assert.assertEquals("Number of connections active/busy should be "+con.length,con.length,datasource.getPool().getActive());
this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1);
Thread.sleep(2500);
Assert.assertTrue("Number of connections should be less than 50.", (datasource.getPool().getActive()<50));
this.datasource.getPoolProperties().setAbandonWhenPercentageFull(0);
Thread.sleep(2500);
Assert.assertEquals("Number of connections active/busy should be "+0,0,datasource.getPool().getActive());
}
}

View File

@@ -0,0 +1,151 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.sql.PooledConnection;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.test.driver.Connection;
import org.apache.tomcat.jdbc.test.driver.Driver;
public class AlternateUsernameTest extends DefaultTestCase {
private static final int iterations = 500000; //(new Random(System.currentTimeMillis())).nextInt(1000000)+100000;
@Test
public void testUsernameCompare() throws Exception {
testUsername(true);
}
private void testUsername(boolean allowUsernameChange) throws Exception {
long start = System.currentTimeMillis();
int withoutuser =10;
int withuser = withoutuser;
this.datasource.setMaxActive(withuser+withoutuser);
this.datasource.setDriverClassName(Driver.class.getName());
this.datasource.setUrl("jdbc:tomcat:test");
this.datasource.setAlternateUsernameAllowed(allowUsernameChange);
this.datasource.getConnection().close();
TestRunner[] runners = new TestRunner[withuser+withoutuser];
for (int i=0; i<withuser; i++) {
TestRunner with = new TestRunner("foo","bar",datasource.getPoolProperties().getUsername(),datasource.getPoolProperties().getPassword());
TestRunner without = new TestRunner(null,null,datasource.getPoolProperties().getUsername(),datasource.getPoolProperties().getPassword());
runners[i] = allowUsernameChange?with:without;
runners[i+withuser] = without;
}
ExecutorService svc = Executors.newFixedThreadPool(withuser+withoutuser);
List<Future<TestResult>> results = svc.invokeAll(Arrays.asList(runners));
int failures = 0;
int total = 0;
for (int i=0; i<withuser; i++) {
failures += results.get(i).get().failures;
total+=results.get(i).get().iterations;
failures += results.get(i+withuser).get().failures;
total+=results.get(i+withuser).get().iterations;
}
long stop = System.currentTimeMillis();
Assert.assertEquals("Nr of failures was:"+failures,0, failures);
svc.shutdownNow();
this.datasource.close();
System.out.println("Nr of connect() calls:"+Driver.connectCount.get());
System.out.println("Nr of disconnect() calls:"+Driver.disconnectCount.get());
System.out.println("Nr of iterations:"+total+" over "+(stop-start)+ " ms.");
}
@Test
public void testUsernameCompareAgain() throws Exception {
testUsernameCompare();
}
@Test
public void testUsernameCompareNotAllowed() throws Exception {
testUsername(false);
}
public static class TestResult {
public int iterations;
public int failures;
public String lastMessage;
}
public class TestRunner implements Callable<TestResult> {
String username;
String password;
volatile boolean done = false;
TestResult result = null;
boolean useuser = true;
public TestRunner(String user, String pass, String guser, String gpass) {
username = user==null?guser : user;
password = pass==null?gpass : pass;
useuser = user!=null;
}
@Override
public TestResult call() {
TestResult test = new TestResult();
PooledConnection pcon = null;
for (int i=0; (!done) && (i<iterations); i++) {
test.iterations = i+1;
try {
pcon = useuser ? (PooledConnection)AlternateUsernameTest.this.datasource.getConnection(username, password) :
(PooledConnection)AlternateUsernameTest.this.datasource.getConnection();
Connection con = (Connection)pcon.getConnection();
Assert.assertTrue("Username mismatch: Requested User:"+username+" Actual user:"+con.getUsername(), con.getUsername().equals(username));
Assert.assertTrue("Password mismatch: Requested Password:"+password+" Actual password:"+con.getPassword(), con.getPassword().equals(password));
}catch (SQLException x) {
test.failures++;
test.lastMessage = x.getMessage();
done = true;
x.printStackTrace();
}catch (Exception x) {
test.failures++;
test.lastMessage = x.getMessage();
x.printStackTrace();
} finally {
if (pcon!=null) {
try {
pcon.close();
} catch (Exception ignore) {
// Ignore
}
pcon = null;
}
}
}
done = true;
result = test;
return result;
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
public class Async0IdleTestBug50477 extends DefaultTestCase {
@Test
public void testAsync0Idle0Size() throws Exception {
System.out.println("[testPoolThreads20Connections10FairAsync] Starting fairness - Tomcat JDBC - Fair - Async");
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setFairQueue(true);
this.datasource.getPoolProperties().setInitialSize(0);
try {
Future<Connection> cf = ((DataSourceProxy)datasource).getConnectionAsync();
cf.get(5, TimeUnit.SECONDS);
}finally {
tearDown();
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Assert;
import org.junit.Test;
public class BorrowWaitTest extends DefaultTestCase {
@Test
public void testWaitTime() throws Exception {
int wait = 10000;
this.datasource.setMaxActive(1);
this.datasource.setMaxWait(wait);
Connection con = datasource.getConnection();
long start = System.currentTimeMillis();
try {
Connection con2 = datasource.getConnection();
Assert.assertFalse("This should not happen, connection should be unavailable.",true);
con2.close();
}catch (SQLException x) {
long delta = System.currentTimeMillis() - start;
boolean inrange = Math.abs(wait-delta) <= 1000;
Assert.assertTrue("Connection should have been acquired within +/- 1 second, but was "+(wait-delta)+" ms.",inrange);
}
con.close();
}
public void testWaitTimeInfinite() throws Exception {
if(true){
System.err.println("testWaitTimeInfinite() test is disabled.");
return;//this would lock up the test suite
}
/*
int wait = -1;
this.datasource.setMaxActive(1);
this.datasource.setMaxWait(wait);
Connection con = datasource.getConnection();
long start = System.currentTimeMillis();
try {
Connection con2 = datasource.getConnection();
Assert.assertFalse("This should not happen, connection should be unavailable.",true);
}catch (SQLException x) {
long delta = System.currentTimeMillis();
boolean inrange = Math.abs(wait-delta) < 1000;
Assert.assertTrue("Connection should have been acquired within +/- 1 second.",true);
}
con.close();
*/
}
}

View File

@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import org.junit.Before;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
public class Bug50571 extends DefaultTestCase{
@Before
public void setUp() throws Exception {
this.datasource.setUrl("jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=FALSE");
this.datasource.setJdbcInterceptors(ConnectionState.class.getName());
this.datasource.setDefaultTransactionIsolation(-55);
this.datasource.setInitialSize(1);
}
@Test
public void testBug50571() throws Exception {
this.datasource.getConnection().close();
}
}

View File

@@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.util.concurrent.Future;
import org.junit.Assert;
import org.junit.Test;
public class Bug50805 extends DefaultTestCase {
@Test
public void test50805() throws Exception {
this.datasource.setInitialSize(0);
this.datasource.setMaxActive(10);
this.datasource.setMinIdle(1);
Assert.assertEquals("Current size should be 0.", 0, this.datasource.getSize());
this.datasource.getConnection().close();
Assert.assertEquals("Current size should be 1.", 1, this.datasource.getSize());
Assert.assertEquals("Idle size should be 1.", 1, this.datasource.getIdle());
Assert.assertEquals("Busy size should be 0.", 0, this.datasource.getActive());
Future<Connection> fc = this.datasource.getConnectionAsync();
Connection con = fc.get();
Assert.assertEquals("Current size should be 1.", 1, this.datasource.getSize());
Assert.assertEquals("Idle size should be 0.", 0, this.datasource.getIdle());
Assert.assertEquals("Busy size should be 1.", 1, this.datasource.getActive());
con.close();
Assert.assertEquals("Current size should be 1.", 1, this.datasource.getSize());
Assert.assertEquals("Idle size should be 1.", 1, this.datasource.getIdle());
Assert.assertEquals("Busy size should be 0.", 0, this.datasource.getActive());
}
}

View File

@@ -0,0 +1,409 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.util.concurrent.CountDownLatch;
import javax.sql.DataSource;
import org.junit.Test;
public class CheckOutThreadTest extends DefaultTestCase {
CountDownLatch latch = null;
@Test
public void testDBCPThreads10Connections10() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.threadcount = 10;
this.transferProperties();
this.tDatasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-dbcp-"+i);
t.d = this.tDatasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testDBCPThreads10Connections10]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
@Test
public void testPoolThreads10Connections10() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setFairQueue(false);
this.threadcount = 10;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-pool-"+i);
t.d = this.datasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testPoolThreads10Connections10]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
@Test
public void testPoolThreads10Connections10Fair() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setFairQueue(true);
this.threadcount = 10;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-pool-"+i);
t.d = this.datasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testPoolThreads10Connections10Fair]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
// @Test
// public void testC3P0Threads10Connections10() throws Exception {
// this.datasource.getPoolProperties().setMaxActive(10);
// this.threadcount = 10;
// this.transferPropertiesToC3P0();
// this.c3p0Datasource.getConnection().close();
// latch = new CountDownLatch(threadcount);
// long start = System.currentTimeMillis();
// for (int i=0; i<threadcount; i++) {
// TestThread t = new TestThread();
// t.setName("tomcat-pool-"+i);
// t.d = this.c3p0Datasource;
// t.start();
// }
// latch.await();
// long delta = System.currentTimeMillis() - start;
// System.out.println("[testC3P0Threads10Connections10]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
// tearDown();
// }
@Test
public void testDBCPThreads20Connections10() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.threadcount = 20;
this.transferProperties();
this.tDatasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-dbcp-"+i);
t.d = this.tDatasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testDBCPThreads20Connections10]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
@Test
public void testPoolThreads20Connections10() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setFairQueue(false);
this.threadcount = 20;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-pool-"+i);
t.d = this.datasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testPoolThreads20Connections10]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
@Test
public void testPoolThreads20Connections10Fair() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setFairQueue(true);
this.threadcount = 20;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-pool-"+i);
t.d = this.datasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testPoolThreads20Connections10Fair]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
// @Test
// public void testC3P0Threads20Connections10() throws Exception {
// this.datasource.getPoolProperties().setMaxActive(10);
// this.threadcount = 20;
// this.transferPropertiesToC3P0();
// this.c3p0Datasource.getConnection().close();
// latch = new CountDownLatch(threadcount);
// long start = System.currentTimeMillis();
// for (int i=0; i<threadcount; i++) {
// TestThread t = new TestThread();
// t.setName("tomcat-pool-"+i);
// t.d = this.c3p0Datasource;
// t.start();
// }
// latch.await();
// long delta = System.currentTimeMillis() - start;
// System.out.println("[testC3P0Threads20Connections10]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
// tearDown();
// }
@Test
public void testDBCPThreads10Connections10Validate() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setTestOnBorrow(true);
this.threadcount = 10;
this.transferProperties();
this.tDatasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-dbcp-validate-"+i);
t.d = this.tDatasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testDBCPThreads10Connections10Validate]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
@Test
public void testPoolThreads10Connections10Validate() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setTestOnBorrow(true);
this.datasource.getPoolProperties().setFairQueue(false);
this.threadcount = 10;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-pool-validate-"+i);
t.d = this.datasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testPoolThreads10Connections10Validate]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
@Test
public void testPoolThreads10Connections10ValidateFair() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setTestOnBorrow(true);
this.datasource.getPoolProperties().setFairQueue(true);
this.threadcount = 10;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-pool-validate-"+i);
t.d = this.datasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testPoolThreads10Connections10ValidateFair]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
// @Test
// public void testC3P0Threads10Connections10Validate() throws Exception {
// this.datasource.getPoolProperties().setMaxActive(10);
// this.datasource.getPoolProperties().setTestOnBorrow(true);
// this.threadcount = 10;
// this.transferPropertiesToC3P0();
// this.c3p0Datasource.getConnection().close();
// latch = new CountDownLatch(threadcount);
// long start = System.currentTimeMillis();
// for (int i=0; i<threadcount; i++) {
// TestThread t = new TestThread();
// t.setName("tomcat-pool-validate-"+i);
// t.d = this.c3p0Datasource;
// t.start();
// }
// latch.await();
// long delta = System.currentTimeMillis() - start;
// System.out.println("[testC3P0Threads10Connections10Validate]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
// tearDown();
// }
@Test
public void testDBCPThreads20Connections10Validate() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setTestOnBorrow(true);
this.threadcount = 20;
this.transferProperties();
this.tDatasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-dbcp-validate-"+i);
t.d = this.tDatasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testDBCPThreads20Connections10Validate]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
@Test
public void testPoolThreads10Connections20Validate() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setTestOnBorrow(true);
this.datasource.getPoolProperties().setFairQueue(false);
this.threadcount = 20;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-pool-validate-"+i);
t.d = this.datasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testPoolThreads20Connections10Validate]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
@Test
public void testPoolThreads10Connections20ValidateFair() throws Exception {
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setTestOnBorrow(true);
this.datasource.getPoolProperties().setFairQueue(true);
this.threadcount = 20;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
for (int i=0; i<threadcount; i++) {
TestThread t = new TestThread();
t.setName("tomcat-pool-validate-"+i);
t.d = this.datasource;
t.start();
}
latch.await();
long delta = System.currentTimeMillis() - start;
System.out.println("[testPoolThreads20Connections10ValidateFair]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
tearDown();
}
// @Test
// public void testC3P0Threads10Connections20Validate() throws Exception {
// this.datasource.getPoolProperties().setMaxActive(10);
// this.datasource.getPoolProperties().setTestOnBorrow(true);
// this.threadcount = 20;
// this.transferPropertiesToC3P0();
// this.c3p0Datasource.getConnection().close();
// latch = new CountDownLatch(threadcount);
// long start = System.currentTimeMillis();
// for (int i=0; i<threadcount; i++) {
// TestThread t = new TestThread();
// t.setName("tomcat-pool-validate-"+i);
// t.d = this.c3p0Datasource;
// t.start();
// }
// latch.await();
// long delta = System.currentTimeMillis() - start;
// System.out.println("[testC3P0Threads10Connections20Validate]Test complete:"+delta+" ms. Iterations:"+(threadcount*this.iterations));
// tearDown();
// }
public class TestThread extends Thread {
protected DataSource d;
@Override
public void run() {
long max = -1, totalmax=0, totalcmax=0, cmax = -1, nroffetch = 0, totalruntime = 0;
try {
for (int i = 0; i < CheckOutThreadTest.this.iterations; i++) {
long start = System.nanoTime();
Connection con = null;
try {
con = d.getConnection();
long delta = System.nanoTime() - start;
totalmax += delta;
max = Math.max(delta, max);
nroffetch++;
} finally {
long cstart = System.nanoTime();
if (con!=null) try {con.close();}catch(Exception x) {x.printStackTrace();}
long cdelta = System.nanoTime() - cstart;
totalcmax += cdelta;
cmax = Math.max(cdelta, cmax);
}
totalruntime+=(System.nanoTime()-start);
}
} catch (Exception x) {
x.printStackTrace();
} finally {
CheckOutThreadTest.this.latch.countDown();
}
if (System.getProperty("print-thread-stats")!=null) {
System.out.println("["+getName()+"] "+
"\n\tMax time to retrieve connection:"+(max/1000f/1000f)+" ms."+
"\n\tTotal time to retrieve connection:"+(totalmax/1000f/1000f)+" ms."+
"\n\tAverage time to retrieve connection:"+(totalmax/1000f/1000f)/nroffetch+" ms."+
"\n\tMax time to close connection:"+(cmax/1000f/1000f)+" ms."+
"\n\tTotal time to close connection:"+(totalcmax/1000f/1000f)+" ms."+
"\n\tAverage time to close connection:"+(totalcmax/1000f/1000f)/nroffetch+" ms."+
"\n\tRun time:"+(totalruntime/1000f/1000f)+" ms."+
"\n\tNr of fetch:"+nroffetch);
}
}
}
}

View File

@@ -0,0 +1,282 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.junit.After;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
import org.apache.tomcat.jdbc.test.driver.Driver;
public class ConnectCountTest extends DefaultTestCase {
protected boolean run = true;
protected long sleep = Long.getLong("sleep", 10).longValue();
protected long complete = Long.getLong("complete",20000).longValue();
protected boolean printthread = Boolean.getBoolean("printthread");
CountDownLatch latch = null;
@Override
public org.apache.tomcat.jdbc.pool.DataSource createDefaultDataSource() {
// TODO Auto-generated method stub
org.apache.tomcat.jdbc.pool.DataSource ds = super.createDefaultDataSource();
ds.getPoolProperties().setDriverClassName(Driver.class.getName());
ds.getPoolProperties().setUrl(Driver.url);
ds.getPoolProperties().setInitialSize(0);
ds.getPoolProperties().setMaxIdle(10);
ds.getPoolProperties().setMinIdle(10);
ds.getPoolProperties().setMaxActive(10);
return ds;
}
@Override
@After
public void tearDown() throws Exception {
Driver.reset();
super.tearDown();
}
protected void printThreadResults(TestThread[] threads, String name, int active, int expected) {
long minfetch = Long.MAX_VALUE, maxfetch = Long.MIN_VALUE, totalfetch = 0;
long maxwait = 0, minwait = Long.MAX_VALUE, totalwait = 0;
for (int i=0; i<threads.length; i++) {
TestThread t = threads[i];
totalfetch += t.nroffetch;
totalwait += t.totalwait;
maxwait = Math.max(maxwait,t.maxwait);
minwait = Math.min(minwait, t.minwait);
minfetch = Math.min(minfetch, t.nroffetch);
maxfetch = Math.max(maxfetch, t.nroffetch);
if (ConnectCountTest.this.printthread)
System.out.println(t.getName()+" : Nr-of-fetch:"+t.nroffetch+ " Max fetch Time:"+t.maxwait/1000000f+"ms. :Max close time:"+t.cmax/1000000f+"ms.");
}
System.out.println("["+name+"] Max fetch:"+(maxfetch)+" Min fetch:"+(minfetch)+" Average fetch:"+
(((float)totalfetch))/(float)threads.length);
System.out.println("["+name+"] Max wait:"+maxwait/1000000f+"ms. Min wait:"+minwait/1000000f+"ms. Average wait:"+(((((float)totalwait))/(float)totalfetch)/1000000f)+" ms.");
System.out.println("["+name+"] Max active:"+active+" Expected Active:"+expected);
}
@Test
public void testDBCPThreads20Connections10() throws Exception {
System.out.println("[testDBCPThreads20Connections10] Starting fairness - DBCP");
this.threadcount = 20;
this.transferProperties();
this.tDatasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
TestThread[] threads = new TestThread[threadcount];
for (int i=0; i<threadcount; i++) {
threads[i] = new TestThread();
threads[i].setName("tomcat-dbcp-"+i);
threads[i].d = this.tDatasource;
}
for (int i=0; i<threadcount; i++) {
threads[i].start();
}
if (!latch.await(complete+1000,TimeUnit.MILLISECONDS)) {
System.out.println("Latch timed out.");
}
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testDBCPThreads20Connections10",Driver.connectCount.get(),10);
System.out.println("Test completed in: " + delta + "ms.");
}
@Test
public void testPoolThreads20Connections10() throws Exception {
System.out.println("[testPoolThreads20Connections10] Starting fairness - Tomcat JDBC - Non Fair");
this.threadcount = 20;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
TestThread[] threads = new TestThread[threadcount];
for (int i=0; i<threadcount; i++) {
threads[i] = new TestThread();
threads[i].setName("tomcat-pool-"+i);
threads[i].d = this.datasource;
}
for (int i=0; i<threadcount; i++) {
threads[i].start();
}
if (!latch.await(complete+1000,TimeUnit.MILLISECONDS)) {
System.out.println("Latch timed out.");
}
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10",Driver.connectCount.get(),10);
System.out.println("Test completed in: " + delta + "ms.");
}
@Test
public void testPoolThreads20Connections10Fair() throws Exception {
System.out.println("[testPoolThreads20Connections10Fair] Starting fairness - Tomcat JDBC - Fair");
this.threadcount = 20;
this.datasource.getPoolProperties().setFairQueue(true);
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
TestThread[] threads = new TestThread[threadcount];
for (int i=0; i<threadcount; i++) {
threads[i] = new TestThread();
threads[i].setName("tomcat-pool-"+i);
threads[i].d = this.datasource;
}
for (int i=0; i<threadcount; i++) {
threads[i].start();
}
if (!latch.await(complete+1000,TimeUnit.MILLISECONDS)) {
System.out.println("Latch timed out.");
}
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10Fair",Driver.connectCount.get(),10);
System.out.println("Test completed in: " + delta + "ms.");
}
@Test
public void testPoolThreads20Connections10FairAsync() throws Exception {
System.out.println("[testPoolThreads20Connections10FairAsync] Starting fairness - Tomcat JDBC - Fair - Async");
this.threadcount = 20;
this.datasource.getPoolProperties().setFairQueue(true);
this.datasource.getPoolProperties().setInitialSize(this.datasource.getPoolProperties().getMaxActive());
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
TestThread[] threads = new TestThread[threadcount];
for (int i=0; i<threadcount; i++) {
threads[i] = new TestThread();
threads[i].setName("tomcat-pool-"+i);
threads[i].async = true;
threads[i].d = this.datasource;
}
for (int i=0; i<threadcount; i++) {
threads[i].start();
}
if (!latch.await(complete+1000,TimeUnit.MILLISECONDS)) {
System.out.println("Latch timed out.");
}
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10FairAsync",Driver.connectCount.get(),10);
System.out.println("Test completed in: " + delta + "ms.");
}
// @Test
// public void testC3P0Threads20Connections10() throws Exception {
// System.out.println("[testC3P0Threads20Connections10] Starting fairness - C3P0");
// this.threadcount = 20;
// this.transferPropertiesToC3P0();
// this.datasource.getConnection().close();
// latch = new CountDownLatch(threadcount);
// long start = System.currentTimeMillis();
// TestThread[] threads = new TestThread[threadcount];
// for (int i=0; i<threadcount; i++) {
// threads[i] = new TestThread();
// threads[i].setName("tomcat-pool-"+i);
// threads[i].d = this.c3p0Datasource;
//
// }
// for (int i=0; i<threadcount; i++) {
// threads[i].start();
// }
// if (!latch.await(complete+1000,TimeUnit.MILLISECONDS)) {
// System.out.println("Latch timed out.");
// }
// this.run = false;
// long delta = System.currentTimeMillis() - start;
// printThreadResults(threads,"testC3P0Threads20Connections10",Driver.connectCount.get(),10);
// }
public class TestThread extends Thread {
protected DataSource d;
protected long sleep = 10;
protected boolean async = false;
long minwait = Long.MAX_VALUE, maxwait = -1, totalwait=0, totalcmax=0, cmax = -1, nroffetch = 0, totalruntime = 0;
@Override
public void run() {
try {
long now = System.currentTimeMillis();
while (ConnectCountTest.this.run) {
if ((System.currentTimeMillis()-now)>=ConnectCountTest.this.complete) break;
long start = System.nanoTime();
Connection con = null;
try {
if (async) {
Future<Connection> cf = ((DataSourceProxy)d).getConnectionAsync();
con = cf.get();
} else {
con = d.getConnection();
}
long delta = System.nanoTime() - start;
totalwait += delta;
maxwait = Math.max(delta, maxwait);
minwait = Math.min(delta, minwait);
nroffetch++;
try {
if (ConnectCountTest.this.sleep>0) sleep(ConnectCountTest.this.sleep);
} catch (InterruptedException x) {
interrupted();
}
} finally {
long cstart = System.nanoTime();
if (con!=null) try {con.close();}catch(Exception x) {x.printStackTrace();}
long cdelta = System.nanoTime() - cstart;
totalcmax += cdelta;
cmax = Math.max(cdelta, cmax);
}
totalruntime+=(System.nanoTime()-start);
}
} catch (RuntimeException | SQLException | ExecutionException | InterruptedException x) {
x.printStackTrace();
} finally {
ConnectCountTest.this.latch.countDown();
}
if (System.getProperty("print-thread-stats")!=null) {
System.out.println("["+getName()+"] "+
"\n\tMax time to retrieve connection:"+maxwait/1000000f+" ms."+
"\n\tTotal time to retrieve connection:"+totalwait/1000000f+" ms."+
"\n\tAverage time to retrieve connection:"+totalwait/1000000f/nroffetch+" ms."+
"\n\tMax time to close connection:"+cmax/1000000f+" ms."+
"\n\tTotal time to close connection:"+totalcmax/1000000f+" ms."+
"\n\tAverage time to close connection:"+totalcmax/1000000f/nroffetch+" ms."+
"\n\tRun time:"+totalruntime/1000000f+" ms."+
"\n\tNr of fetch:"+nroffetch);
}
}
}
}

View File

@@ -0,0 +1,137 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Random;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer;
public class CreateTestTable extends DefaultTestCase {
public static volatile boolean recreate = Boolean.getBoolean("recreate");
@Test
public void testCreateTestTable() throws Exception {
Connection con = datasource.getConnection();
Statement st = con.createStatement();
try {
st.execute("create table test(id int not null, val1 varchar(255), val2 varchar(255), val3 varchar(255), val4 varchar(255))");
} catch (Exception ignore) {
// Ignore
}
st.close();
con.close();
}
public int testCheckData() throws Exception {
int count = 0;
String check = "select count (*) from test";
Connection con = datasource.getConnection();
Statement st = con.createStatement();
try {
ResultSet rs = st.executeQuery(check);
if (rs.next())
count = rs.getInt(1);
rs.close();
st.close();
System.out.println("Count:"+count);
}catch (Exception ignore) {}
con.close();
return count;
}
@Test
public void testPopulateData() throws Exception {
int count = 100000;
int actual = testCheckData();
if (actual>=count) {
System.out.println("Test tables has "+actual+" rows of data. No need to populate.");
return;
}
datasource.setJdbcInterceptors(ResetAbandonedTimer.class.getName());
String insert = "insert into test values (?,?,?,?,?)";
this.datasource.setRemoveAbandoned(false);
Connection con = datasource.getConnection();
boolean commit = con.getAutoCommit();
con.setAutoCommit(false);
if (recreate) {
Statement st = con.createStatement();
try {
st.execute("drop table test");
} catch (Exception ignore) {
// Ignore
}
st.execute("create table test(id int not null, val1 varchar(255), val2 varchar(255), val3 varchar(255), val4 varchar(255))");
st.close();
}
PreparedStatement ps = con.prepareStatement(insert);
ps.setQueryTimeout(0);
for (int i=actual; i<count; i++) {
ps.setInt(1,i);
String s = getRandom();
ps.setString(2, s);
ps.setString(3, s);
ps.setString(4, s);
ps.setString(5, s);
ps.addBatch();
ps.clearParameters();
if ((i+1) % 1000 == 0) {
System.out.print(".");
}
if ((i+1) % 10000 == 0) {
System.out.print("\n"+(i+1));
ps.executeBatch();
ps.close();
con.commit();
ps = con.prepareStatement(insert);
ps.setQueryTimeout(0);
}
}
ps.close();
con.setAutoCommit(commit);
con.close();
}
public static final Random random = new Random(System.currentTimeMillis());
public static String getRandom() {
StringBuilder s = new StringBuilder(256);
for (int i=0;i<254; i++) {
int b = Math.abs(random.nextInt() % 29);
char c = (char)(b+65);
s.append(c);
}
return s.toString();
}
public static void main(String[] args) throws Exception {
recreate = true;
CreateTestTable test = new CreateTestTable();
test.testPopulateData();
}
}

View File

@@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.util.Properties;
import org.apache.tomcat.jdbc.pool.DataSourceFactory;
import org.apache.tomcat.jdbc.pool.PoolProperties;
/**
* @version 1.0
*/
public class DefaultProperties extends PoolProperties {
private static final long serialVersionUID = 1L;
public DefaultProperties() {
setDbProperties(new Properties());
//mysql
//url = System.getProperty("url","jdbc:mysql://localhost:3306/mysql?autoReconnect=true");
//driverClassName = System.getProperty("driverClassName","com.mysql.jdbc.Driver");
//derby
//url = System.getProperty("url","jdbc:derby:derbyDB;create=true");
//driverClassName = System.getProperty("driverClassName","org.apache.derby.jdbc.EmbeddedDriver");
setUrl(System.getProperty("url","jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=FALSE"));
setDriverClassName(System.getProperty("driverClassName","org.h2.Driver"));
System.setProperty("h2.serverCachedObjects", "10000");
setPassword(System.getProperty("password","password"));
setUsername(System.getProperty("username","root"));
setValidationQuery(System.getProperty("validationQuery","SELECT 1"));
setDefaultAutoCommit(Boolean.TRUE);
setDefaultReadOnly(Boolean.FALSE);
setDefaultTransactionIsolation(DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION);
setConnectionProperties(null);
setDefaultCatalog(null);
setInitialSize(10);
setMaxActive(100);
setMaxIdle(getInitialSize());
setMinIdle(getInitialSize());
setMaxWait(10000);
setTestOnBorrow(true);
setTestOnReturn(false);
setTestWhileIdle(true);
setTimeBetweenEvictionRunsMillis(5000);
setNumTestsPerEvictionRun(0);
setMinEvictableIdleTimeMillis(1000);
setRemoveAbandoned(true);
setRemoveAbandonedTimeout(5000);
setLogAbandoned(true);
setValidationInterval(0); //always validate
setInitSQL(null);
setTestOnConnect(false);
getDbProperties().setProperty("user",getUsername());
getDbProperties().setProperty("password",getPassword());
}
}

View File

@@ -0,0 +1,268 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.junit.After;
import org.junit.Before;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.pool.PoolProperties;
/**
* @version 1.0
*/
public abstract class DefaultTestCase {
protected org.apache.tomcat.jdbc.pool.DataSource datasource;
protected BasicDataSource tDatasource;
// protected ComboPooledDataSource c3p0Datasource;
protected int threadcount = 10;
protected int iterations = 100000;
public org.apache.tomcat.jdbc.pool.DataSource createDefaultDataSource() {
org.apache.tomcat.jdbc.pool.DataSource datasource = null;
PoolConfiguration p = new DefaultProperties();
p.setFairQueue(false);
p.setJmxEnabled(false);
p.setTestWhileIdle(false);
p.setTestOnBorrow(false);
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(threadcount);
p.setInitialSize(threadcount);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(10);
p.setMinEvictableIdleTimeMillis(10000);
p.setMinIdle(threadcount);
p.setLogAbandoned(false);
p.setRemoveAbandoned(false);
datasource = new org.apache.tomcat.jdbc.pool.DataSource();
datasource.setPoolProperties(p);
return datasource;
}
@Before
public void init() throws Exception {
this.datasource = createDefaultDataSource();
}
protected void transferProperties() {
try {
Properties p = new Properties();
for (int i=0; i< ALL_PROPERTIES.length; i++) {
String property = ALL_PROPERTIES[i];
String dbcpProperty = handleRenames(property);
String name = "get" + Character.toUpperCase(property.charAt(0)) + property.substring(1);
String bname = "is" + name.substring(3);
Method get = null;
try {
get = PoolProperties.class.getMethod(name, new Class[0]);
}catch (NoSuchMethodException x) {
try {
get = PoolProperties.class.getMethod(bname, new Class[0]);
}catch (NoSuchMethodException x2) {
String msg = x2.getMessage();
if (msg.indexOf("isPoolPreparedStatements")>=0) {
//noop - ignore known missing properties
} else if (msg.indexOf("isMaxOpenPreparedStatements")>=0) {
//noop - ignore known missing properties
} else {
System.err.println("Missing property:"+x2.getMessage());
}
}
}
if (get!=null) {
Object value = get.invoke(datasource.getPoolProperties(), new Object[0]);
if (value!=null) {
p.setProperty(dbcpProperty, value.toString());
}
}
}
tDatasource = BasicDataSourceFactory.createDataSource(p);
}catch (Exception x) {
x.printStackTrace();
}
}
private String handleRenames(String oldName) {
if (RENAMED.containsKey(oldName)) {
return RENAMED.get(oldName);
}
return oldName;
}
protected void transferPropertiesToC3P0() throws Exception {
// System.setProperty("com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL", "WARNING");
// MLog.getLogger().setLevel(MLevel.WARNING);
// MLog.getLogger("com").setLevel(MLevel.WARNING);
// //http://www.mchange.com/projects/c3p0/index.html#automaticTestTable
// ComboPooledDataSource c3p0 = new ComboPooledDataSource();
// c3p0.setAcquireIncrement(1);
// c3p0.setAcquireRetryAttempts(2);
// c3p0.setAcquireRetryDelay(datasource.getPoolProperties().getMaxWait());
// c3p0.setCheckoutTimeout(datasource.getPoolProperties().getMaxWait());
// c3p0.setDebugUnreturnedConnectionStackTraces(datasource.getPoolProperties().isLogAbandoned());
// c3p0.setIdleConnectionTestPeriod(datasource.getPoolProperties().getTimeBetweenEvictionRunsMillis()/1000);
// c3p0.setInitialPoolSize(datasource.getPoolProperties().getInitialSize());
// c3p0.setMaxIdleTime(datasource.getPoolProperties().getMinEvictableIdleTimeMillis()/1000);
// c3p0.setMaxIdleTimeExcessConnections(datasource.getPoolProperties().getMaxIdle());
// c3p0.setMaxPoolSize(datasource.getPoolProperties().getMaxActive());
// c3p0.setMinPoolSize(datasource.getPoolProperties().getMinIdle());
// c3p0.setPassword(datasource.getPoolProperties().getPassword());
// c3p0.setPreferredTestQuery(datasource.getPoolProperties().getValidationQuery());
// c3p0.setTestConnectionOnCheckin(datasource.getPoolProperties().isTestOnReturn());
// c3p0.setTestConnectionOnCheckout(datasource.getPoolProperties().isTestOnBorrow());
// c3p0.setUnreturnedConnectionTimeout(datasource.getPoolProperties().getRemoveAbandonedTimeout());
// c3p0.setUser(datasource.getPoolProperties().getUsername());
// c3p0.setUsesTraditionalReflectiveProxies(true);
// c3p0.setJdbcUrl(datasource.getPoolProperties().getUrl());
// c3p0.setDriverClass(datasource.getPoolProperties().getDriverClassName());
// this.c3p0Datasource = c3p0;
/**
acquireIncrement
acquireRetryAttempts
acquireRetryDelay
autoCommitOnClose
automaticTestTable
breakAfterAcquireFailure
checkoutTimeout
connectionCustomizerClassName
connectionTesterClassName
debugUnreturnedConnectionStackTraces
factoryClassLocation
forceIgnoreUnresolvedTransactions
idleConnectionTestPeriod
initialPoolSize
maxAdministrativeTaskTime
maxConnectionAge
maxIdleTime
maxIdleTimeExcessConnections
maxPoolSize
maxStatements
maxStatementsPerConnection
minPoolSize
numHelperThreads
overrideDefaultUser
overrideDefaultPassword
password
preferredTestQuery
propertyCycle
testConnectionOnCheckin
testConnectionOnCheckout
unreturnedConnectionTimeout
user
usesTraditionalReflectiveProxies
*/
}
@After
public void tearDown() throws Exception {
try {
datasource.close();
} catch (Exception ignore){
// Ignore
}
try {
tDatasource.close();
} catch (Exception ignore){
// Ignore
}
//try {((ComboPooledDataSource)c3p0Datasource).close(true);}catch(Exception ignore){}
datasource = null;
tDatasource = null;
//c3p0Datasource = null;
System.gc();
org.apache.tomcat.jdbc.test.driver.Driver.reset();
}
private static final String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit";
private static final String PROP_DEFAULTREADONLY = "defaultReadOnly";
private static final String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation";
private static final String PROP_DEFAULTCATALOG = "defaultCatalog";
private static final String PROP_DRIVERCLASSNAME = "driverClassName";
private static final String PROP_MAXACTIVE = "maxActive";
private static final String PROP_MAXIDLE = "maxIdle";
private static final String PROP_MINIDLE = "minIdle";
private static final String PROP_INITIALSIZE = "initialSize";
private static final String PROP_MAXWAIT = "maxWait";
private static final String PROP_TESTONBORROW = "testOnBorrow";
private static final String PROP_TESTONRETURN = "testOnReturn";
private static final String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis";
private static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun";
private static final String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis";
private static final String PROP_TESTWHILEIDLE = "testWhileIdle";
private static final String PROP_PASSWORD = "password";
private static final String PROP_URL = "url";
private static final String PROP_USERNAME = "username";
private static final String PROP_VALIDATIONQUERY = "validationQuery";
private static final String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed";
private static final String PROP_REMOVEABANDONED = "removeAbandoned";
private static final String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout";
private static final String PROP_LOGABANDONED = "logAbandoned";
private static final String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements";
private static final String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements";
private static final String PROP_CONNECTIONPROPERTIES = "connectionProperties";
private static final String[] ALL_PROPERTIES = {
PROP_DEFAULTAUTOCOMMIT,
PROP_DEFAULTREADONLY,
PROP_DEFAULTTRANSACTIONISOLATION,
PROP_DEFAULTCATALOG,
PROP_DRIVERCLASSNAME,
PROP_MAXACTIVE,
PROP_MAXIDLE,
PROP_MINIDLE,
PROP_INITIALSIZE,
PROP_MAXWAIT,
PROP_TESTONBORROW,
PROP_TESTONRETURN,
PROP_TIMEBETWEENEVICTIONRUNSMILLIS,
PROP_NUMTESTSPEREVICTIONRUN,
PROP_MINEVICTABLEIDLETIMEMILLIS,
PROP_TESTWHILEIDLE,
PROP_PASSWORD,
PROP_URL,
PROP_USERNAME,
PROP_VALIDATIONQUERY,
PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED,
PROP_REMOVEABANDONED,
PROP_REMOVEABANDONEDTIMEOUT,
PROP_LOGABANDONED,
PROP_POOLPREPAREDSTATEMENTS,
PROP_MAXOPENPREPAREDSTATEMENTS,
PROP_CONNECTIONPROPERTIES
};
private static final Map<String,String> RENAMED = new HashMap<>();
static {
RENAMED.put(PROP_MAXACTIVE, "maxTotal");
}
}

View File

@@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import javax.sql.PooledConnection;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.tomcat.jdbc.test.driver.Driver;
public class EqualsHashCodeTest extends DefaultTestCase{
public static final String password = "password";
public static final String username = "username";
@Before
public void setUp() throws Exception {
this.datasource.setDriverClassName(Driver.class.getName());
this.datasource.setUrl("jdbc:tomcat:test");
this.datasource.setPassword(password);
this.datasource.setMaxActive(1);
this.datasource.setMinIdle(datasource.getMaxActive());
this.datasource.setMaxIdle(datasource.getMaxActive());
this.datasource.setUsername(username);
this.datasource.getConnection().close();
datasource.createPool();
}
@Test
public void testEquals() throws Exception {
Connection con1 = datasource.getConnection();
Connection real1 = ((PooledConnection)con1).getConnection();
Assert.assertEquals(con1, con1);
con1.close();
Assert.assertEquals(con1, con1);
Connection con2 = datasource.getConnection();
Connection real2 = ((PooledConnection)con2).getConnection();
Assert.assertEquals(real1,real2);
Assert.assertEquals(con2, con2);
Assert.assertNotSame(con1, con2);
con2.close();
Assert.assertEquals(con2, con2);
}
@Test
public void testHashCode() throws Exception {
Connection con1 = datasource.getConnection();
Assert.assertEquals(con1.hashCode(), con1.hashCode());
con1.close();
Assert.assertEquals(con1.hashCode(), con1.hashCode());
Connection con2 = datasource.getConnection();
Assert.assertEquals(con2.hashCode(), con2.hashCode());
Assert.assertTrue(con1.hashCode() != con2.hashCode());
con2.close();
Assert.assertEquals(con2.hashCode(), con2.hashCode());
}
}

View File

@@ -0,0 +1,264 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
public class FairnessTest extends DefaultTestCase {
protected boolean run = true;
protected long sleep = Long.getLong("sleep", 10).longValue();
protected long complete = Long.getLong("complete",20000).longValue();
protected boolean printthread = Boolean.getBoolean("printthread");
CountDownLatch latch = null;
protected void printThreadResults(TestThread[] threads, String name, int active, int expected) {
long minfetch = Long.MAX_VALUE, maxfetch = Long.MIN_VALUE, totalfetch = 0;
long maxwait = 0, minwait = Long.MAX_VALUE, totalwait = 0;
for (int i=0; i<threads.length; i++) {
TestThread t = threads[i];
totalfetch += t.nroffetch;
totalwait += t.totalwait;
maxwait = Math.max(maxwait,t.maxwait);
minwait = Math.min(minwait, t.minwait);
minfetch = Math.min(minfetch, t.nroffetch);
maxfetch = Math.max(maxfetch, t.nroffetch);
if (FairnessTest.this.printthread)
System.out.println(t.getName()+" : Nr-of-fetch:"+t.nroffetch+ " Max fetch Time:"+t.maxwait/1000000f+"ms. :Max close time:"+t.cmax/1000000f+"ms.");
}
System.out.println("["+name+"] Max fetch:"+(maxfetch)+" Min fetch:"+(minfetch)+" Average fetch:"+
(((float)totalfetch))/(float)threads.length);
System.out.println("["+name+"] Max wait:"+maxwait/1000000f+"ms. Min wait:"+minwait/1000000f+"ms. Average wait:"+(((((float)totalwait))/(float)totalfetch)/1000000f)+" ms.");
System.out.println("["+name+"] Max active:"+active+" Expected Active:"+expected);
}
@Test
public void testDBCPThreads20Connections10() throws Exception {
System.out.println("[testDBCPThreads20Connections10] Starting fairness - DBCP");
this.datasource.getPoolProperties().setMaxActive(10);
this.threadcount = 20;
this.transferProperties();
this.tDatasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
TestThread[] threads = new TestThread[threadcount];
for (int i=0; i<threadcount; i++) {
threads[i] = new TestThread();
threads[i].setName("tomcat-dbcp-"+i);
threads[i].d = this.tDatasource;
}
for (int i=0; i<threadcount; i++) {
threads[i].start();
}
if (!latch.await(complete+1000,TimeUnit.MILLISECONDS)) {
System.out.println("Latch timed out.");
}
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testDBCPThreads20Connections10",this.tDatasource.getNumActive(),10);
System.out.println("Test completed in: " + delta + "ms.");
}
@Test
public void testPoolThreads20Connections10() throws Exception {
System.out.println("[testPoolThreads20Connections10] Starting fairness - Tomcat JDBC - Non Fair");
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setFairQueue(false);
this.threadcount = 20;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
TestThread[] threads = new TestThread[threadcount];
for (int i=0; i<threadcount; i++) {
threads[i] = new TestThread();
threads[i].setName("tomcat-pool-"+i);
threads[i].d = this.datasource;
}
for (int i=0; i<threadcount; i++) {
threads[i].start();
}
if (!latch.await(complete+1000,TimeUnit.MILLISECONDS)) {
System.out.println("Latch timed out.");
}
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10",this.datasource.getSize(),10);
System.out.println("Test completed in: " + delta + "ms.");
}
@Test
public void testPoolThreads20Connections10Fair() throws Exception {
System.out.println("[testPoolThreads20Connections10Fair] Starting fairness - Tomcat JDBC - Fair");
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setFairQueue(true);
this.threadcount = 20;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
TestThread[] threads = new TestThread[threadcount];
for (int i=0; i<threadcount; i++) {
threads[i] = new TestThread();
threads[i].setName("tomcat-pool-"+i);
threads[i].d = this.datasource;
}
for (int i=0; i<threadcount; i++) {
threads[i].start();
}
if (!latch.await(complete+1000,TimeUnit.MILLISECONDS)) {
System.out.println("Latch timed out.");
}
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10Fair",this.datasource.getSize(),10);
System.out.println("Test completed in: " + delta + "ms.");
}
@Test
public void testPoolThreads20Connections10FairAsync() throws Exception {
System.out.println("[testPoolThreads20Connections10FairAsync] Starting fairness - Tomcat JDBC - Fair - Async");
this.datasource.getPoolProperties().setMaxActive(10);
this.datasource.getPoolProperties().setFairQueue(true);
this.threadcount = 20;
this.transferProperties();
this.datasource.getConnection().close();
latch = new CountDownLatch(threadcount);
long start = System.currentTimeMillis();
TestThread[] threads = new TestThread[threadcount];
for (int i=0; i<threadcount; i++) {
threads[i] = new TestThread();
threads[i].setName("tomcat-pool-"+i);
threads[i].async = true;
threads[i].d = this.datasource;
}
for (int i=0; i<threadcount; i++) {
threads[i].start();
}
if (!latch.await(complete+1000,TimeUnit.MILLISECONDS)) {
System.out.println("Latch timed out.");
}
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10FairAsync",this.datasource.getSize(),10);
System.out.println("Test completed in: " + delta + "ms.");
}
// @Test
// public void testC3P0Threads20Connections10() throws Exception {
// System.out.println("[testC3P0Threads20Connections10] Starting fairness - C3P0");
// this.datasource.getPoolProperties().setMaxActive(10);
// this.datasource.getPoolProperties().setFairQueue(false);
// this.threadcount = 20;
// this.transferPropertiesToC3P0();
// this.datasource.getConnection().close();
// latch = new CountDownLatch(threadcount);
// long start = System.currentTimeMillis();
// TestThread[] threads = new TestThread[threadcount];
// for (int i=0; i<threadcount; i++) {
// threads[i] = new TestThread();
// threads[i].setName("tomcat-pool-"+i);
// threads[i].d = this.c3p0Datasource;
//
// }
// for (int i=0; i<threadcount; i++) {
// threads[i].start();
// }
// if (!latch.await(complete+1000,TimeUnit.MILLISECONDS)) {
// System.out.println("Latch timed out.");
// }
// this.run = false;
// long delta = System.currentTimeMillis() - start;
// printThreadResults(threads,"testC3P0Threads20Connections10",c3p0Datasource.getNumConnectionsAllUsers(),10);
// }
public class TestThread extends Thread {
protected DataSource d;
protected long sleep = 10;
protected boolean async = false;
long minwait = Long.MAX_VALUE, maxwait = -1, totalwait=0, totalcmax=0, cmax = -1, nroffetch = 0, totalruntime = 0;
@Override
public void run() {
try {
long now = System.currentTimeMillis();
while (FairnessTest.this.run) {
if ((System.currentTimeMillis()-now)>=FairnessTest.this.complete) break;
long start = System.nanoTime();
Connection con = null;
try {
if (async) {
Future<Connection> cf = ((DataSourceProxy)d).getConnectionAsync();
con = cf.get();
} else {
con = d.getConnection();
}
long delta = System.nanoTime() - start;
totalwait += delta;
maxwait = Math.max(delta, maxwait);
minwait = Math.min(delta, minwait);
nroffetch++;
try {
if (FairnessTest.this.sleep>0) sleep(FairnessTest.this.sleep);
} catch (InterruptedException x) {
interrupted();
}
} finally {
long cstart = System.nanoTime();
if (con!=null) try {con.close();}catch(Exception x) {x.printStackTrace();}
long cdelta = System.nanoTime() - cstart;
totalcmax += cdelta;
cmax = Math.max(cdelta, cmax);
}
totalruntime+=(System.nanoTime()-start);
}
} catch (RuntimeException | SQLException | ExecutionException | InterruptedException x) {
x.printStackTrace();
} finally {
FairnessTest.this.latch.countDown();
}
if (System.getProperty("print-thread-stats")!=null) {
System.out.println("["+getName()+"] "+
"\n\tMax time to retrieve connection:"+maxwait/1000000f+" ms."+
"\n\tTotal time to retrieve connection:"+totalwait/1000000f+" ms."+
"\n\tAverage time to retrieve connection:"+totalwait/1000000f/nroffetch+" ms."+
"\n\tMax time to close connection:"+cmax/1000000f+" ms."+
"\n\tTotal time to close connection:"+totalcmax/1000000f+" ms."+
"\n\tAverage time to close connection:"+totalcmax/1000000f/nroffetch+" ms."+
"\n\tRun time:"+totalruntime/1000000f+" ms."+
"\n\tNr of fetch:"+nroffetch);
}
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.lang.management.ManagementFactory;
import java.util.Hashtable;
import java.util.Properties;
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.PoolUtilities;
import org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean;
import org.apache.tomcat.jdbc.test.driver.Driver;
public class JmxPasswordTest extends DefaultTestCase{
public static final String password = "password";
public static final String username = "username";
public ObjectName oname = null;
@Before
public void setUp() throws Exception {
this.datasource.setDriverClassName(Driver.class.getName());
this.datasource.setUrl("jdbc:tomcat:test");
this.datasource.setPassword(password);
this.datasource.setUsername(username);
this.datasource.getConnection().close();
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
String domain = "tomcat.jdbc";
Hashtable<String,String> properties = new Hashtable<>();
properties.put("type", "ConnectionPool");
properties.put("class", this.getClass().getName());
oname = new ObjectName(domain,properties);
ConnectionPool pool = datasource.createPool();
org.apache.tomcat.jdbc.pool.jmx.ConnectionPool jmxPool = new org.apache.tomcat.jdbc.pool.jmx.ConnectionPool(pool);
mbs.registerMBean(jmxPool, oname);
}
@Test
public void testPassword() throws Exception {
Assert.assertEquals("Passwords should match when not using JMX.",password,datasource.getPoolProperties().getPassword());
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ConnectionPoolMBean mbean = JMX.newMBeanProxy(mbs, oname, ConnectionPoolMBean.class);
String jmxPassword = mbean.getPassword();
Properties jmxProperties = mbean.getDbProperties();
Assert.assertFalse("Passwords should not match.", password.equals(jmxPassword));
Assert.assertFalse("Password property should be missing", jmxProperties.containsKey(PoolUtilities.PROP_PASSWORD));
}
}

View File

@@ -0,0 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.test.driver.Driver;
public class MultipleCloseTest extends DefaultTestCase {
@Override
public org.apache.tomcat.jdbc.pool.DataSource createDefaultDataSource() {
org.apache.tomcat.jdbc.pool.DataSource ds = super.createDefaultDataSource();
ds.getPoolProperties().setDriverClassName(Driver.class.getName());
ds.getPoolProperties().setUrl(Driver.url);
ds.getPoolProperties().setInitialSize(0);
ds.getPoolProperties().setMaxIdle(1);
ds.getPoolProperties().setMinIdle(1);
ds.getPoolProperties().setMaxActive(1);
ds.getPoolProperties().setUseDisposableConnectionFacade(true);
return ds;
}
@After
@Override
public void tearDown() throws Exception {
Driver.reset();
super.tearDown();
}
@Test
public void testClosedConnectionsNotReused() throws Exception {
this.init();
Connection con1 = datasource.getConnection();
// A new connection is open
Assert.assertFalse(con1.isClosed());
con1.close();
// Confirm that a closed connection is closed
Assert.assertTrue(con1.isClosed());
// Open a new connection (This will re-use the previous pooled connection)
Connection con2 = datasource.getConnection();
// A connection, once closed, should stay closed
Assert.assertTrue(con1.isClosed());
con2.close();
}
}

View File

@@ -0,0 +1,99 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.DataSource;
public class PoolCleanerTest extends DefaultTestCase {
private int countPoolCleanerThreads() {
Map<Thread, StackTraceElement[]> threadmap = Thread.getAllStackTraces();
int result = 0;
for (Thread t : threadmap.keySet()) {
if (t.getName().startsWith("Tomcat JDBC Pool Cleaner[")) result++;
}
return result;
}
@Test
public void testPoolCleaner() throws Exception {
datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(2000);
datasource.getPoolProperties().setTestWhileIdle(true);
Assert.assertEquals("Pool cleaner should not be started yet.",0,ConnectionPool.getPoolCleaners().size() );
Assert.assertNull("Pool timer should be null", ConnectionPool.getPoolTimer());
Assert.assertEquals("Pool cleaner threads should not be present.",0, countPoolCleanerThreads());
datasource.getConnection().close();
Assert.assertEquals("Pool cleaner should have 1 cleaner.",1,ConnectionPool.getPoolCleaners().size() );
Assert.assertNotNull("Pool timer should not be null", ConnectionPool.getPoolTimer());
Assert.assertEquals("Pool cleaner threads should be 1.",1, countPoolCleanerThreads());
datasource.close();
Assert.assertEquals("Pool shutdown, no cleaners should be present.",0,ConnectionPool.getPoolCleaners().size() );
Assert.assertNull("Pool timer should be null after shutdown", ConnectionPool.getPoolTimer());
Assert.assertEquals("Pool cleaner threads should not be present after close.",0, countPoolCleanerThreads());
}
@Test
public void test2PoolCleaners() throws Exception {
datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(2000);
datasource.getPoolProperties().setTestWhileIdle(true);
DataSource ds2 = new DataSource(datasource.getPoolProperties());
Assert.assertEquals("Pool cleaner should not be started yet.",0,ConnectionPool.getPoolCleaners().size() );
Assert.assertNull("Pool timer should be null", ConnectionPool.getPoolTimer());
Assert.assertEquals("Pool cleaner threads should not be present.",0, countPoolCleanerThreads());
datasource.getConnection().close();
ds2.getConnection().close();
Assert.assertEquals("Pool cleaner should have 2 cleaner.",2,ConnectionPool.getPoolCleaners().size() );
Assert.assertNotNull("Pool timer should not be null", ConnectionPool.getPoolTimer());
Assert.assertEquals("Pool cleaner threads should be 1.",1, countPoolCleanerThreads());
datasource.close();
Assert.assertEquals("Pool cleaner should have 1 cleaner.",1,ConnectionPool.getPoolCleaners().size() );
Assert.assertNotNull("Pool timer should not be null", ConnectionPool.getPoolTimer());
ds2.close();
Assert.assertEquals("Pool shutdown, no cleaners should be present.",0,ConnectionPool.getPoolCleaners().size() );
Assert.assertNull("Pool timer should be null after shutdown", ConnectionPool.getPoolTimer());
Assert.assertEquals("Pool cleaner threads should not be present after close.",0, countPoolCleanerThreads());
}
@Test
public void testIdleTimeout() throws Exception {
datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(2000);
datasource.getPoolProperties().setTestWhileIdle(true);
datasource.getPoolProperties().setMaxIdle(0);
datasource.getPoolProperties().setInitialSize(0);
datasource.getPoolProperties().setMinIdle(0);
datasource.getPoolProperties().setMinEvictableIdleTimeMillis(1000);
datasource.getConnection().close();
Assert.assertEquals("Pool should have 1 idle.", 1, datasource.getIdle());
Thread.sleep(3000);
Assert.assertEquals("Pool should have 0 idle.", 0, datasource.getIdle());
}
}

View File

@@ -0,0 +1,86 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.test.driver.Driver;
public class PoolPurgeTest extends DefaultTestCase {
static final int expectedSize = 2;
@Override
public org.apache.tomcat.jdbc.pool.DataSource createDefaultDataSource() {
// TODO Auto-generated method stub
org.apache.tomcat.jdbc.pool.DataSource ds = super.createDefaultDataSource();
ds.getPoolProperties().setDriverClassName(Driver.class.getName());
ds.getPoolProperties().setUrl(Driver.url);
ds.getPoolProperties().setInitialSize(expectedSize);
ds.getPoolProperties().setMaxIdle(expectedSize);
ds.getPoolProperties().setMinIdle(expectedSize);
ds.getPoolProperties().setMaxActive(expectedSize);
ds.getPoolProperties().setTimeBetweenEvictionRunsMillis(30000);
ds.getPoolProperties().setMaxAge(Long.MAX_VALUE);
return ds;
}
@Override
@After
public void tearDown() throws Exception {
Driver.reset();
super.tearDown();
}
@Test
public void testPoolPurge() throws Exception {
this.datasource.getConnection().close();
Assert.assertEquals("Nr of connections should be "+expectedSize, expectedSize , datasource.getSize());
this.datasource.purge();
Assert.assertEquals("Nr of connections should be 0", 0 , datasource.getSize());
}
@Test
public void testPoolPurgeWithActive() throws Exception {
Connection con = datasource.getConnection();
Assert.assertEquals("Nr of connections should be "+expectedSize, expectedSize , datasource.getSize());
this.datasource.purge();
Assert.assertEquals("Nr of connections should be "+(expectedSize-1), (expectedSize-1) , datasource.getSize());
con.close();
Assert.assertEquals("Nr of connections should be 0", 0 , datasource.getSize());
}
@Test
public void testPoolPurgeOnReturn() throws Exception {
init();
Connection con = datasource.getConnection();
Assert.assertEquals("Nr of connections should be "+expectedSize, expectedSize , datasource.getSize());
this.datasource.purgeOnReturn();
Assert.assertEquals("Nr of connections should be "+expectedSize, expectedSize , datasource.getSize());
con.close();
Assert.assertEquals("Nr of connections should be "+(expectedSize-1), (expectedSize-1) , datasource.getSize());
tearDown();
}
}

View File

@@ -0,0 +1,85 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.concurrent.Future;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class SimplePOJOAsyncExample {
public static void main(String[] args) throws Exception {
PoolConfiguration p = new PoolProperties();
p.setFairQueue(true);
p.setUrl("jdbc:mysql://localhost:3306/mysql?autoReconnect=true");
p.setDriverClassName("com.mysql.jdbc.Driver");
p.setUsername("root");
p.setPassword("password");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
DataSource datasource = new DataSource();
datasource.setPoolProperties(p);
Connection con = null;
try {
Future<Connection> future = datasource.getConnectionAsync();
while (!future.isDone()) {
System.out.println("Connection is not yet available. Do some background work");
try {
Thread.sleep(100); //simulate work
} catch (InterruptedException x) {
Thread.interrupted();
}
}
con = future.get(); //should return instantly
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from user");
int cnt = 1;
while (rs.next()) {
System.out.println((cnt++)+". Host:" +rs.getString("Host")+" User:"+rs.getString("User")+" Password:"+rs.getString("Password"));
}
rs.close();
st.close();
} finally {
if (con!=null) {
try {
con.close();
} catch (Exception ignore) {
// Ignore
}
}
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class SimplePOJOExample {
public static void main(String[] args) throws Exception {
PoolConfiguration p = new PoolProperties();
p.setUrl("jdbc:mysql://localhost:3306/mysql?autoReconnect=true");
p.setDriverClassName("com.mysql.jdbc.Driver");
p.setUsername("root");
p.setPassword("password");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
DataSource datasource = new DataSource();
datasource.setPoolProperties(p);
Connection con = null;
try {
con = datasource.getConnection();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from user");
int cnt = 1;
while (rs.next()) {
System.out.println((cnt++)+". Host:" +rs.getString("Host")+" User:"+rs.getString("User")+" Password:"+rs.getString("Password"));
}
rs.close();
st.close();
} finally {
if (con!=null) {
try {
con.close();
} catch (Exception ignore) {
// Ignore
}
}
}
}
}

View File

@@ -0,0 +1,110 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Assert;
import org.junit.Test;
/**
* If a connection is abandoned and closed,
* then that should free up a spot in the pool, and other threads
* that are waiting should not time out and throw an error but be
* able to acquire a connection, since one was just released.
*/
public class StarvationTest extends DefaultTestCase {
private void config() {
datasource.getPoolProperties().setMaxActive(1);
datasource.getPoolProperties().setMaxIdle(1);
datasource.getPoolProperties().setInitialSize(1);
datasource.getPoolProperties().setRemoveAbandoned(true);
datasource.getPoolProperties().setRemoveAbandonedTimeout(5);
datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(500);
datasource.getPoolProperties().setMaxWait(10000);
datasource.getPoolProperties().setLogAbandoned(true);
}
// @Test
// public void testDBCPConnectionStarvation() throws Exception {
// config();
// this.transferProperties();
// this.tDatasource.getConnection().close();
// javax.sql.DataSource datasource = this.tDatasource;
// Connection con1 = datasource.getConnection();
// Connection con2 = null;
// try {
// con2 = datasource.getConnection();
// try {
// con2.setCatalog("mysql");//make sure connection is valid
// }catch (SQLException x) {
// Assert.assertFalse("2nd Connection is not valid:"+x.getMessage(),true);
// }
// Assert.assertTrue("Connection 1 should be closed.",con1.isClosed()); //first connection should be closed
// }catch (Exception x) {
// Assert.assertFalse("Connection got starved:"+x.getMessage(),true);
// }finally {
// if (con2!=null) con2.close();
// }
// }
@Test
public void testConnectionStarvation() throws Exception {
config();
Connection con1 = datasource.getConnection();
Connection con2 = null;
try {
con2 = datasource.getConnection();
try {
con2.setCatalog("mysql");//make sure connection is valid
}catch (SQLException x) {
Assert.assertFalse("2nd Connection is not valid:"+x.getMessage(),true);
}
Assert.assertTrue("Connection 1 should be closed.",con1.isClosed()); //first connection should be closed
}catch (Exception x) {
Assert.assertFalse("Connection got starved:"+x.getMessage(),true);
}finally {
if (con2!=null) con2.close();
}
con1.close();
}
@Test
public void testFairConnectionStarvation() throws Exception {
init();
config();
datasource.getPoolProperties().setFairQueue(true);
Connection con1 = datasource.getConnection();
Connection con2 = null;
try {
con2 = datasource.getConnection();
try {
con2.setCatalog("mysql");//make sure connection is valid
}catch (SQLException x) {
Assert.assertFalse("2nd Connection is not valid:"+x.getMessage(),true);
}
Assert.assertTrue("Connection 1 should be closed.",con1.isClosed()); //first connection should be closed
}catch (Exception x) {
Assert.assertFalse("Connection got starved:"+x.getMessage(),true);
}finally {
if (con2!=null) con2.close();
}
con1.close();
}
}

View File

@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.Statement;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer;
public class StatementFinalizerTest extends DefaultTestCase {
@Test
public void testStatementFinalization() throws Exception {
datasource.setJdbcInterceptors(StatementFinalizer.class.getName());
Connection con = datasource.getConnection();
Statement st = con.createStatement();
Assert.assertFalse("Statement should not be closed.",st.isClosed());
con.close();
Assert.assertTrue("Statement should be closed.",st.isClosed());
}
@Test
public void testStatementFinalizationForMultiple() throws Exception {
datasource.setJdbcInterceptors(StatementFinalizer.class.getName());
Connection con = datasource.getConnection();
Statement[] statements = new Statement[1000];
for (int i = 0; i < statements.length; i++) {
statements[i] = con.createStatement();
}
for (Statement st : statements) {
Assert.assertFalse("Statement should not be closed.", st.isClosed());
}
con.close();
for (Statement st : statements) {
Assert.assertTrue("Statement should be closed.", st.isClosed());
}
}
}

View File

@@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.FairBlockingQueue;
public class TestAsyncQueue {
protected FairBlockingQueue<Object> queue = null;
@Before
public void setUp() throws Exception {
this.queue = new FairBlockingQueue<>();
}
@After
public void tearDown() throws Exception {
this.queue = null;
}
@Test
public void testAsyncPoll1() throws Exception {
Object item = new Object();
queue.offer(item);
Future<Object> future = queue.pollAsync();
Assert.assertEquals(future.get(),item);
}
@Test
public void testAsyncPoll2() throws Exception {
Object item = new Object();
OfferThread thread = new OfferThread(item,5000);
thread.start();
Future<Object> future = queue.pollAsync();
try {
future.get(2000, TimeUnit.MILLISECONDS);
Assert.assertFalse("Request should have timed out",true);
}catch (TimeoutException x) {
Assert.assertTrue("Request timed out properly",true);
}catch (Exception x) {
Assert.assertTrue("Request threw an error",false);
x.printStackTrace();
}
Assert.assertEquals(future.get(),item);
}
protected class OfferThread extends Thread {
Object item = null;
long delay = 5000;
volatile boolean offered = false;
public OfferThread(Object i, long d) {
this.item = i;
this.delay = d;
this.setDaemon(false);
this.setName(TestAsyncQueue.class.getName()+"-OfferThread");
}
@Override
public void run() {
try {
sleep(delay);
} catch (Exception ignore){
// Ignore
}
offered = true;
TestAsyncQueue.this.queue.offer(item);
}
}
}

View File

@@ -0,0 +1,216 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.test.driver.Driver;
public class TestConcurrency extends DefaultTestCase {
public static final boolean debug = Boolean.getBoolean("jdbc.debug");
protected volatile DataSource ds = null;
@Before
public void setUp() throws Exception {
ds = createDefaultDataSource();
ds.getPoolProperties().setDriverClassName(Driver.class.getName());
ds.getPoolProperties().setUrl(Driver.url);
ds.getPoolProperties().setInitialSize(0);
ds.getPoolProperties().setMaxIdle(0);
ds.getPoolProperties().setMinIdle(0);
ds.getPoolProperties().setMaxActive(10);
ds.getPoolProperties().setRemoveAbandoned(true);
ds.getPoolProperties().setLogAbandoned(true);
ds.getPoolProperties().setTestWhileIdle(true);
ds.getPoolProperties().setMinEvictableIdleTimeMillis(750);
ds.getPoolProperties().setTimeBetweenEvictionRunsMillis(25);
ds.setFairQueue(true);
}
@Override
@After
public void tearDown() throws Exception {
ds.close(true);
Driver.reset();
super.tearDown();
}
@Test
public void testSimple() throws Exception {
ds.getConnection().close();
final int iter = 1000 * 10;
final AtomicInteger loopcount = new AtomicInteger(0);
final Runnable run = new Runnable() {
@Override
public void run() {
try {
while (loopcount.incrementAndGet() < iter) {
Connection con = ds.getConnection();
Thread.sleep(10);
con.close();
}
}catch (Exception x) {
loopcount.set(iter); //stops the test
x.printStackTrace();
}
}
};
Thread[] threads = new Thread[20];
for (int i=0; i<threads.length; i++) {
threads[i] = new Thread(run);
}
for (int i=0; i<threads.length; i++) {
threads[i].start();
}
try {
while (loopcount.get()<iter) {
Assert.assertTrue("Size comparison(less than 11):",ds.getPool().getSize()<=10);
if (debug) {
System.out.println("Size: "+ds.getPool().getSize());
System.out.println("Used: "+ds.getPool().getActive());
System.out.println("Idle: "+ds.getPool().getIdle());
System.out.println("Wait: "+ds.getPool().getWaitCount());
}
Thread.sleep(250);
}
}catch (Exception x) {
loopcount.set(iter); //stops the test
x.printStackTrace();
}
for (int i=0; i<threads.length; i++) {
threads[i].join();
}
Assert.assertEquals("Size comparison:",10, ds.getPool().getSize());
Assert.assertEquals("Idle comparison:",10, ds.getPool().getIdle());
Assert.assertEquals("Used comparison:",0, ds.getPool().getActive());
Assert.assertEquals("Connect count",10,Driver.connectCount.get());
}
@Test
public void testBrutal() throws Exception {
ds.getPoolProperties().setRemoveAbandoned(false);
ds.getPoolProperties().setRemoveAbandonedTimeout(1);
ds.getPoolProperties().setMinEvictableIdleTimeMillis(100);
ds.getPoolProperties().setTimeBetweenEvictionRunsMillis(10);
ds.getConnection().close();
final int iter = 100000 * 10;
final AtomicInteger loopcount = new AtomicInteger(0);
final Runnable run = new Runnable() {
@Override
public void run() {
try {
while (loopcount.incrementAndGet() < iter) {
Connection con = ds.getConnection();
con.close();
}
}catch (Exception x) {
loopcount.set(iter); //stops the test
x.printStackTrace();
}
}
};
Thread[] threads = new Thread[20];
for (int i=0; i<threads.length; i++) {
threads[i] = new Thread(run);
}
for (int i=0; i<threads.length; i++) {
threads[i].start();
}
try {
while (loopcount.get()<iter) {
Assert.assertTrue("Size comparison(less than 11):",ds.getPool().getSize()<=10);
ds.getPool().testAllIdle();
ds.getPool().checkAbandoned();
ds.getPool().checkIdle();
}
}catch (Exception x) {
loopcount.set(iter); //stops the test
x.printStackTrace();
}
for (int i=0; i<threads.length; i++) {
threads[i].join();
}
System.out.println("Connect count:"+Driver.connectCount.get());
System.out.println("DisConnect count:"+Driver.disconnectCount.get());
Assert.assertEquals("Size comparison:",10, ds.getPool().getSize());
Assert.assertEquals("Idle comparison:",10, ds.getPool().getIdle());
Assert.assertEquals("Used comparison:",0, ds.getPool().getActive());
Assert.assertEquals("Connect count",10,Driver.connectCount.get());
}
@Test
public void testBrutalNonFair() throws Exception {
ds.getPoolProperties().setRemoveAbandoned(false);
ds.getPoolProperties().setRemoveAbandonedTimeout(1);
ds.getPoolProperties().setMinEvictableIdleTimeMillis(100);
ds.getPoolProperties().setTimeBetweenEvictionRunsMillis(10);
ds.getConnection().close();
final int iter = 100000 * 10;
final AtomicInteger loopcount = new AtomicInteger(0);
final Runnable run = new Runnable() {
@Override
public void run() {
try {
while (loopcount.incrementAndGet() < iter) {
Connection con = ds.getConnection();
con.close();
}
}catch (Exception x) {
loopcount.set(iter); //stops the test
x.printStackTrace();
}
}
};
Thread[] threads = new Thread[20];
for (int i=0; i<threads.length; i++) {
threads[i] = new Thread(run);
}
for (int i=0; i<threads.length; i++) {
threads[i].start();
}
try {
while (loopcount.get()<iter) {
Assert.assertTrue("Size comparison(less than 11):",ds.getPool().getSize()<=10);
ds.getPool().testAllIdle();
ds.getPool().checkAbandoned();
ds.getPool().checkIdle();
}
}catch (Exception x) {
loopcount.set(iter); //stops the test
x.printStackTrace();
}
for (int i=0; i<threads.length; i++) {
threads[i].join();
}
System.out.println("Connect count:"+Driver.connectCount.get());
System.out.println("DisConnect count:"+Driver.disconnectCount.get());
Assert.assertEquals("Size comparison:",10, ds.getPool().getSize());
Assert.assertEquals("Idle comparison:",10, ds.getPool().getIdle());
Assert.assertEquals("Used comparison:",0, ds.getPool().getActive());
Assert.assertEquals("Connect count",10,Driver.connectCount.get());
}
}

View File

@@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
import org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
public class TestConnectionState extends DefaultTestCase {
@Test
public void testAutoCommitFalse() throws Exception {
DataSourceProxy d1 = this.createDefaultDataSource();
d1.setMaxActive(1);
d1.setMinIdle(1);
d1.setMaxIdle(1);
d1.setJdbcInterceptors(ConnectionState.class.getName());
d1.setDefaultAutoCommit(Boolean.FALSE);
Connection c1 = d1.getConnection();
Assert.assertFalse("Auto commit should be false",c1.getAutoCommit());
c1.setAutoCommit(true);
Assert.assertTrue("Auto commit should be true",c1.getAutoCommit());
c1.close();
c1 = d1.getConnection();
Assert.assertFalse("Auto commit should be false for a reused connection",c1.getAutoCommit());
d1.close(true);
Assert.assertTrue("Connection should be closed",c1.isClosed());
}
@Test
public void testAutoCommitTrue() throws Exception {
DataSourceProxy d1 = this.createDefaultDataSource();
d1.setMaxActive(1);
d1.setJdbcInterceptors(ConnectionState.class.getName());
d1.setDefaultAutoCommit(Boolean.TRUE);
d1.setMinIdle(1);
Connection c1 = d1.getConnection();
Assert.assertTrue("Auto commit should be true",c1.getAutoCommit());
c1.setAutoCommit(false);
Assert.assertFalse("Auto commit should be false",c1.getAutoCommit());
c1.close();
c1 = d1.getConnection();
Assert.assertTrue("Auto commit should be true for a reused connection",c1.getAutoCommit());
}
@Test
public void testDefaultCatalog() throws Exception {
DataSourceProxy d1 = this.createDefaultDataSource();
d1.setMaxActive(1);
d1.setJdbcInterceptors(ConnectionState.class.getName());
d1.setDefaultCatalog("information_schema");
d1.setMinIdle(1);
Connection c1 = d1.getConnection();
Assert.assertEquals("Catalog should be information_schema",c1.getCatalog(),"information_schema");
c1.close();
c1 = d1.getConnection();
Assert.assertEquals("Catalog should be information_schema",c1.getCatalog(),"information_schema");
c1.setCatalog("mysql");
Assert.assertEquals("Catalog should be information_schema",c1.getCatalog(),"mysql");
c1.close();
c1 = d1.getConnection();
Assert.assertEquals("Catalog should be information_schema",c1.getCatalog(),"information_schema");
}
}

View File

@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.Statement;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
import org.apache.tomcat.jdbc.pool.PooledConnection;
public class TestException extends DefaultTestCase {
@Test
public void testException() throws Exception {
datasource.getPoolProperties().setJdbcInterceptors(TestInterceptor.class.getName());
Connection con = datasource.getConnection();
try (Statement s = con.createStatement()){
} catch (Exception x) {
Assert.fail();
}
con.close();
}
public static class TestInterceptor extends JdbcInterceptor {
@Override
public void reset(ConnectionPool parent, PooledConnection con) {
// NO-OP
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import org.junit.Test;
public class TestGCClose extends DefaultTestCase {
@Test
public void testGCStop() throws Exception {
datasource.getConnection();
System.out.println("Got a connection, but didn't return it");
tearDown();
Thread.sleep(20000);
}
@Test
public void testClose() throws Exception {
datasource.getConnection();
System.out.println("Got a connection, but didn't return it");
datasource.close(true);
Thread.sleep(20000);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import javax.sql.PooledConnection;
import org.junit.Assert;
import org.junit.Test;
public class TestGetConnection extends DefaultTestCase {
@Test
public void testGetConnection() throws Exception {
Connection con = this.datasource.getConnection();
Assert.assertTrue("Connection should implement javax.sql.PooledConnection",con instanceof PooledConnection);
Connection actual = ((PooledConnection)con).getConnection();
Assert.assertNotNull("Connection delegate should not be null.",actual);
System.out.println("Actual connection:"+actual.getClass().getName());
}
}

View File

@@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.interceptor.TestInterceptor;
public class TestInterceptorShortName extends DefaultTestCase {
@Test
public void testShortInterceptor() throws Exception {
this.datasource = this.createDefaultDataSource();
this.datasource.setJdbcInterceptors("TestInterceptor");
this.datasource.setUseDisposableConnectionFacade(false);
this.datasource.setMaxActive(1);
this.datasource.createPool();
Assert.assertEquals("Only one interceptor should have been called setProperties[1]",1,TestInterceptor.instancecount.get());
TestInterceptor.instancecount.set(0);
Connection con = this.datasource.getConnection();
Assert.assertTrue("Pool should have been started.",TestInterceptor.poolstarted);
Assert.assertEquals("Only one interceptor should have been called setProperties[2]",1,TestInterceptor.instancecount.get());
con.close();
this.datasource.close();
Assert.assertTrue("Pool should have been closed.",TestInterceptor.poolclosed);
}
}

View File

@@ -0,0 +1,174 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorDefinition;
import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
import org.apache.tomcat.jdbc.pool.TrapException;
/**
* Test of JdbcInterceptor configuration parsing in the
* {@link org.apache.tomcat.jdbc.pool.PoolProperties PoolProperties} class.
* Added in context of bug 54395.
*/
public class TestJdbcInterceptorConfigParsing {
@Test
public void testBasic() throws Exception {
String interceptorConfig = "FirstInterceptor;SecondInterceptor(parm1=value1,parm2=value2)";
PoolProperties props = new PoolProperties();
props.setJdbcInterceptors(interceptorConfig);
InterceptorDefinition[] interceptorDefs = props.getJdbcInterceptorsAsArray();
Assert.assertNotNull(interceptorDefs);
// 3 items because parser automatically inserts TrapException interceptor to front of list
Assert.assertEquals(interceptorDefs.length, 3);
Assert.assertEquals(interceptorDefs[0].getClassName(), TrapException.class.getName());
Assert.assertNotNull(interceptorDefs[1]);
Assert.assertEquals(interceptorDefs[1].getClassName(), "FirstInterceptor");
Assert.assertNotNull(interceptorDefs[2]);
Assert.assertEquals(interceptorDefs[2].getClassName(), "SecondInterceptor");
Map<String, InterceptorProperty> secondProps = interceptorDefs[2].getProperties();
Assert.assertNotNull(secondProps);
Assert.assertEquals(secondProps.size(), 2);
Assert.assertNotNull(secondProps.get("parm1"));
Assert.assertEquals(secondProps.get("parm1").getValue(), "value1");
Assert.assertNotNull(secondProps.get("parm2"));
Assert.assertEquals(secondProps.get("parm2").getValue(), "value2");
}
@Test
public void testWhitespace() throws Exception {
String interceptorConfig = "FirstInterceptor ; \n" +
"SecondInterceptor (parm1 = value1 , parm2= value2 ) ; \n\n" +
"\t org.cyb.ThirdInterceptor(parm1=value1); \n" +
"EmptyParmValInterceptor(parm1= )";
PoolProperties props = new PoolProperties();
props.setJdbcInterceptors(interceptorConfig);
InterceptorDefinition[] interceptorDefs = props.getJdbcInterceptorsAsArray();
Assert.assertNotNull(interceptorDefs);
// 5 items because parser automatically inserts TrapException interceptor to front of list
Assert.assertEquals(interceptorDefs.length, 5);
Assert.assertEquals(interceptorDefs[0].getClassName(), TrapException.class.getName());
Assert.assertNotNull(interceptorDefs[1]);
Assert.assertEquals(interceptorDefs[1].getClassName(), "FirstInterceptor");
Assert.assertNotNull(interceptorDefs[2]);
Assert.assertEquals(interceptorDefs[2].getClassName(), "SecondInterceptor");
Assert.assertNotNull(interceptorDefs[3]);
Assert.assertEquals(interceptorDefs[3].getClassName(), "org.cyb.ThirdInterceptor");
Map<String, InterceptorProperty> secondProps = interceptorDefs[2].getProperties();
Assert.assertNotNull(secondProps);
Assert.assertEquals(secondProps.size(), 2);
Assert.assertNotNull(secondProps.get("parm1"));
Assert.assertEquals(secondProps.get("parm1").getValue(), "value1");
Assert.assertNotNull(secondProps.get("parm2"));
Assert.assertEquals(secondProps.get("parm2").getValue(), "value2"); // Bug 54395
Map<String, InterceptorProperty> thirdProps = interceptorDefs[3].getProperties();
Assert.assertNotNull(thirdProps);
Assert.assertEquals(thirdProps.size(), 1);
Assert.assertNotNull(thirdProps.get("parm1"));
Assert.assertEquals(thirdProps.get("parm1").getValue(), "value1");
Map<String, InterceptorProperty> emptyParmValProps = interceptorDefs[4].getProperties();
Assert.assertNotNull(emptyParmValProps);
Assert.assertEquals(emptyParmValProps.size(), 1);
Assert.assertNotNull(emptyParmValProps.get("parm1"));
Assert.assertEquals(emptyParmValProps.get("parm1").getValue(), "");
}
/*
* Some of these should probably be handled more cleanly by the parser, but a few known
* exception scenarios are presented here just to document current behavior. In many cases
* failure in parsing will just be propagated to a definition that will fail later
* when instantiated. Should we be failing faster (and with more detail)?
*/
@Test
public void testExceptionOrNot() throws Exception {
PoolProperties props = null;
String[] exceptionInducingConfigs = {
"EmptyParmsInterceptor()",
"WhitespaceParmsInterceptor( )"
};
for (String badConfig : exceptionInducingConfigs) {
props = new PoolProperties();
props.setJdbcInterceptors(badConfig);
try {
props.getJdbcInterceptorsAsArray();
Assert.fail("Expected exception.");
} catch (Exception e) {
// Expected
}
}
String[] noExceptionButInvalidConfigs = {
"MalformedParmsInterceptor(= )",
"MalformedParmsInterceptor( =)",
"MalformedParmsInterceptor(",
"MalformedParmsInterceptor( ",
"MalformedParmsInterceptor)",
"MalformedParmsInterceptor) ",
"MalformedParmsInterceptor )"
};
for (String badConfig : noExceptionButInvalidConfigs) {
props = new PoolProperties();
props.setJdbcInterceptors(badConfig);
try {
props.getJdbcInterceptorsAsArray();
} catch (Exception e) {
Assert.fail("Unexpected exception.");
}
}
}
@Test
public void textExtraSemicolonBehavior() {
// This one DOES get an extra/empty definition
PoolProperties props = new PoolProperties();
props.setJdbcInterceptors(";EmptyLeadingSemiInterceptor");
InterceptorDefinition[] jiDefs = props.getJdbcInterceptorsAsArray();
Assert.assertNotNull(jiDefs);
Assert.assertEquals(jiDefs.length, 3);
// This one does NOT get an extra/empty definition (no trailing whitespace)
props = new PoolProperties();
props.setJdbcInterceptors("EmptyTrailingSemiInterceptor;");
jiDefs = props.getJdbcInterceptorsAsArray();
Assert.assertNotNull(jiDefs);
Assert.assertEquals(jiDefs.length, 2);
// This one DOES get an extra/empty definition (with trailing whitespace)
props = new PoolProperties();
props.setJdbcInterceptors("EmptyTrailingSemiInterceptor; ");
jiDefs = props.getJdbcInterceptorsAsArray();
Assert.assertNotNull(jiDefs);
Assert.assertEquals(jiDefs.length, 3);
}
}

View File

@@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.Statement;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor;
import org.apache.tomcat.jdbc.test.driver.Driver;
public class TestQueryTimeoutInterceptor extends DefaultTestCase {
@Test
public void testTimeout() throws Exception {
int timeout = 10;
int withoutuser =10;
int withuser = withoutuser;
this.datasource.setMaxActive(withuser+withoutuser);
this.datasource.setJdbcInterceptors(QueryTimeoutInterceptor.class.getName()+"(queryTimeout="+timeout+")");
this.datasource.setDriverClassName(Driver.class.getName());
this.datasource.setUrl("jdbc:tomcat:test");
Connection con = this.datasource.getConnection();
Statement st = con.createStatement();
Assert.assertEquals(st.getClass().getName(),timeout,st.getQueryTimeout());
st.close();
st = con.prepareStatement("");
Assert.assertEquals(st.getClass().getName(),timeout,st.getQueryTimeout());
st.close();
st = con.prepareCall("");
Assert.assertEquals(st.getClass().getName(),timeout,st.getQueryTimeout());
st.close();
con.close();
}
}

View File

@@ -0,0 +1,126 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.test.driver.Driver;
/**
* https://bz.apache.org/bugzilla/show_bug.cgi?id=50613
*/
public class TestSizePreservation {
protected volatile DataSource ds = null;
private void initSimplePoolProperties() {
PoolConfiguration p = new DefaultProperties();
ds = new org.apache.tomcat.jdbc.pool.DataSource();
ds.setPoolProperties(p);
ds.getPoolProperties().setDriverClassName(Driver.class.getName());
ds.getPoolProperties().setUrl(Driver.url);
ds.getPoolProperties().setFairQueue(true);
ds.getPoolProperties().setJmxEnabled(false);
ds.getPoolProperties().setTestWhileIdle(true);
ds.getPoolProperties().setTestOnBorrow(false);
ds.getPoolProperties().setTestOnReturn(false);
ds.getPoolProperties().setValidationInterval(30000);
ds.getPoolProperties().setTimeBetweenEvictionRunsMillis(30000);
ds.getPoolProperties().setInitialSize(100);
ds.getPoolProperties().setMaxActive(100);
ds.getPoolProperties().setMinIdle(0);
ds.getPoolProperties().setMaxIdle(0);
ds.getPoolProperties().setMaxWait(10000);
ds.getPoolProperties().setRemoveAbandonedTimeout(10);
ds.getPoolProperties().setMinEvictableIdleTimeMillis(10000);
ds.getPoolProperties().setLogAbandoned(false);
ds.getPoolProperties().setRemoveAbandoned(false);
ds.getPoolProperties().setUseLock(true);
}
private void initEvictingPool() {
initSimplePoolProperties();
ds.getPoolProperties().setTimeBetweenEvictionRunsMillis(25);
ds.getPoolProperties().setMinEvictableIdleTimeMillis(750);
ds.getPoolProperties().setRemoveAbandoned(true);
ds.getPoolProperties().setRemoveAbandonedTimeout(1);
}
@Test
public void testSimple() throws Exception {
initSimplePoolProperties();
common();
ds.close(true);
Driver.reset();
}
@Test
public void testEvicting() throws Exception {
initEvictingPool();
common();
ds.close(true);
Driver.reset();
}
private void common() throws Exception {
ds.getConnection().close();
final int iterations = 1000;
final AtomicInteger loopcount = new AtomicInteger(0);
final Runnable run = new Runnable() {
@Override
public void run() {
try {
while (loopcount.incrementAndGet() < iterations) {
Connection c = ds.getConnection();
Thread.sleep(1000);
c.close();
}
} catch (Exception x) {
x.printStackTrace();
}
}
};
Thread[] threads = new Thread[200];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(run);
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
try {
while (loopcount.get() < iterations) {
Thread.sleep(250);
}
} catch (Exception x) {
loopcount.set(iterations); // stops the test
x.printStackTrace();
}
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}
System.out.println("Pool size:"+ds.getPool().getSize());
Assert.assertTrue("Size validity check: ", ds.getPool().getSize() >= 0);
}
}

View File

@@ -0,0 +1,122 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport.QueryStats;
public class TestSlowQueryComparator {
@Test
public void testBug58489() throws Exception {
long[] testData = { 0, 0, 0, 1444225382010l, 0, 1444225382011l, 0,
1444225382012l, 0, 1444225382056l, 0, 1444225382014l, 0,
1444225382015l, 0, 1444225382016l, 0, 0, 1444225382017l, 0,
1444225678350l, 0, 1444225680397l, 0, 1444225382018l,
1444225382019l, 1444225382020l, 0, 1444225382021l, 0,
1444225382022l, 1444225382023l
};
List<QueryStats> stats = new ArrayList<>();
for (int i = 0; i < testData.length; i++) {
QueryStats qs = new QueryStats(String.valueOf(i));
qs.add(0, testData[i]);
stats.add(qs);
}
try {
Collections.sort(stats, createComparator());
} catch (IllegalArgumentException e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testEqualQueryStatsWithNoLastInvocation() throws Exception {
Comparator<QueryStats> queryStatsComparator = createComparator();
QueryStats q1 = new QueryStats("abc");
Assert.assertEquals(0, queryStatsComparator.compare(q1, q1));
}
@Test
public void testEqualQueryStatsWithLastInvocation() throws Exception {
Comparator<QueryStats> queryStatsComparator = createComparator();
QueryStats q1 = new QueryStats("abc");
q1.add(0, 100);
Assert.assertEquals(0, queryStatsComparator.compare(q1, q1));
}
@Test
public void testQueryStatsOneWithLastInvocation() throws Exception {
Comparator<QueryStats> queryStatsComparator = createComparator();
QueryStats q1 = new QueryStats("abc");
QueryStats q2 = new QueryStats("def");
q2.add(0, 100);
Assert.assertEquals(1, queryStatsComparator.compare(q1, q2));
Assert.assertEquals(-1, queryStatsComparator.compare(q2, q1));
}
@Test
public void testQueryStatsBothWithSameLastInvocation() throws Exception {
Comparator<QueryStats> queryStatsComparator = createComparator();
QueryStats q1 = new QueryStats("abc");
QueryStats q2 = new QueryStats("def");
q1.add(0, 100);
q2.add(0, 100);
Assert.assertEquals(0, queryStatsComparator.compare(q1, q2));
Assert.assertEquals(0, queryStatsComparator.compare(q2, q1));
}
@Test
public void testQueryStatsBothWithSomeLastInvocation() throws Exception {
Comparator<QueryStats> queryStatsComparator = createComparator();
QueryStats q1 = new QueryStats("abc");
QueryStats q2 = new QueryStats("abc");
q1.add(0, 100);
q2.add(0, 150);
Assert.assertEquals(-1, queryStatsComparator.compare(q1, q2));
Assert.assertEquals(1, queryStatsComparator.compare(q2, q1));
}
private Comparator<QueryStats> createComparator()
throws ClassNotFoundException, InstantiationException,
IllegalAccessException, InvocationTargetException,
SecurityException, NoSuchMethodException {
Class<?> comparatorClass = Class
.forName("org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport$QueryStatsComparator");
Constructor<?> comparatorConstructor = comparatorClass.getConstructor();
comparatorConstructor.setAccessible(true);
@SuppressWarnings("unchecked")
Comparator<QueryStats> queryStatsComparator = (Comparator<QueryStats>) comparatorConstructor
.newInstance();
return queryStatsComparator;
}
}

View File

@@ -0,0 +1,336 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.lang.management.ManagementFactory;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import javax.management.AttributeChangeNotification;
import javax.management.Notification;
import javax.management.NotificationListener;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport;
import org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx;
public class TestSlowQueryReport extends DefaultTestCase {
public static final String superSlowSql = "select count(1) from test where val1 like 'ewq%eq' and val2 = 'ew%rre' and val3 = 'sda%da' and val4 = 'dad%ada'";
public static final String failedSql = "select 1 from non_existent";
@Before
public void setUp() throws SQLException {
DriverManager.registerDriver(new MockDriver());
// use our mock driver
this.datasource.setDriverClassName(MockDriver.class.getName());
this.datasource.setUrl(MockDriver.url);
// Required to trigger validation query's execution
this.datasource.setInitialSize(1);
this.datasource.setTestOnBorrow(true);
this.datasource.setValidationInterval(-1);
this.datasource.setValidationQuery("SELECT 1");
this.datasource.setMaxActive(1);
this.datasource.setJdbcInterceptors(SlowQueryReportJmx.class.getName()+"(threshold=50,notifyPool=false)");
}
@Test
public void testSlowSql() throws Exception {
int count = 3;
this.datasource.setMaxActive(1);
this.datasource.setJdbcInterceptors(SlowQueryReport.class.getName()+"(threshold=50)");
Connection con = this.datasource.getConnection();
for (int i=0; i<count; i++) {
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(superSlowSql);
rs.close();
st.close();
}
Map<String,SlowQueryReport.QueryStats> map = SlowQueryReport.getPoolStats(datasource.getPool().getName());
Assert.assertNotNull(map);
Assert.assertEquals(1,map.size());
String key = map.keySet().iterator().next();
SlowQueryReport.QueryStats stats = map.get(key);
System.out.println("Stats:"+stats);
for (int i=0; i<count; i++) {
PreparedStatement st = con.prepareStatement(superSlowSql);
ResultSet rs = st.executeQuery();
rs.close();
st.close();
}
System.out.println("Stats:"+stats);
for (int i=0; i<count; i++) {
CallableStatement st = con.prepareCall(superSlowSql);
ResultSet rs = st.executeQuery();
rs.close();
st.close();
}
System.out.println("Stats:"+stats);
ConnectionPool pool = datasource.getPool();
con.close();
tearDown();
//make sure we actually did clean up when the pool closed
Assert.assertNull(SlowQueryReport.getPoolStats(pool.getName()));
}
@Test
public void testSlowSqlJmx() throws Exception {
int count = 1;
Connection con = this.datasource.getConnection();
for (int i=0; i<count; i++) {
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(superSlowSql);
rs.close();
st.close();
}
Map<String,SlowQueryReport.QueryStats> map = SlowQueryReport.getPoolStats(datasource.getPool().getName());
Assert.assertNotNull(map);
Assert.assertEquals(1,map.size());
String key = map.keySet().iterator().next();
SlowQueryReport.QueryStats stats = map.get(key);
System.out.println("Stats:"+stats);
ClientListener listener = new ClientListener();
ConnectionPool pool = datasource.getPool();
ManagementFactory.getPlatformMBeanServer().addNotificationListener(
new SlowQueryReportJmx().getObjectName(SlowQueryReportJmx.class, pool.getName()),
listener,
null,
null);
for (int i=0; i<count; i++) {
PreparedStatement st = con.prepareStatement(superSlowSql);
ResultSet rs = st.executeQuery();
rs.close();
st.close();
}
System.out.println("Stats:"+stats);
for (int i=0; i<count; i++) {
CallableStatement st = con.prepareCall(superSlowSql);
ResultSet rs = st.executeQuery();
rs.close();
st.close();
}
System.out.println("Stats:"+stats);
Assert.assertEquals("Expecting to have received "+(2*count)+" notifications.",2*count, listener.notificationCount.get());
con.close();
tearDown();
//make sure we actually did clean up when the pool closed
Assert.assertNull(SlowQueryReport.getPoolStats(pool.getName()));
}
@Test
public void testFastSql() throws Exception {
int count = 3;
Connection con = this.datasource.getConnection();
String fastSql = this.datasource.getValidationQuery();
for (int i=0; i<count; i++) {
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(fastSql);
rs.close();
st.close();
}
Map<String,SlowQueryReport.QueryStats> map = SlowQueryReport.getPoolStats(datasource.getPool().getName());
Assert.assertNotNull(map);
Assert.assertEquals(1,map.size());
ConnectionPool pool = datasource.getPool();
con.close();
tearDown();
Assert.assertNull(SlowQueryReport.getPoolStats(pool.getName()));
}
@Test
public void testFailedSql() throws Exception {
int count = 3;
Connection con = this.datasource.getConnection();
for (int i=0; i<count; i++) {
Statement st = con.createStatement();
try {
ResultSet rs = st.executeQuery(failedSql);
rs.close();
}catch (Exception x) {
// NO-OP
}
st.close();
}
Map<String,SlowQueryReport.QueryStats> map = SlowQueryReport.getPoolStats(datasource.getPool().getName());
Assert.assertNotNull(map);
Assert.assertEquals(1,map.size());
ConnectionPool pool = datasource.getPool();
String key = map.keySet().iterator().next();
SlowQueryReport.QueryStats stats = map.get(key);
System.out.println("Stats:"+stats);
con.close();
tearDown();
Assert.assertNull(SlowQueryReport.getPoolStats(pool.getName()));
}
public class ClientListener implements NotificationListener {
AtomicInteger notificationCount = new AtomicInteger(0);
@Override
public void handleNotification(Notification notification,
Object handback) {
notificationCount.incrementAndGet();
System.out.println("\nReceived notification:");
System.out.println("\tClassName: " + notification.getClass().getName());
System.out.println("\tSource: " + notification.getSource());
System.out.println("\tType: " + notification.getType());
System.out.println("\tMessage: " + notification.getMessage());
if (notification instanceof AttributeChangeNotification) {
AttributeChangeNotification acn =
(AttributeChangeNotification) notification;
System.out.println("\tAttributeName: " + acn.getAttributeName());
System.out.println("\tAttributeType: " + acn.getAttributeType());
System.out.println("\tNewValue: " + acn.getNewValue());
System.out.println("\tOldValue: " + acn.getOldValue());
}
}
}
/**
* Mock Driver, Connection and Statement implementations use to verify setQueryTimeout was called.
*/
public static class MockDriver implements java.sql.Driver {
public static final String url = "jdbc:tomcat:mock";
public MockDriver() {
}
@Override
public boolean acceptsURL(String url) throws SQLException {
return url!=null && url.equals(MockDriver.url);
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
return new MockConnection(info);
}
@Override
public int getMajorVersion() {
return 0;
}
@Override
public int getMinorVersion() {
return 0;
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
return null;
}
@Override
public boolean jdbcCompliant() {
return false;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
public static class MockConnection extends org.apache.tomcat.jdbc.test.driver.Connection {
public MockConnection(Properties info) {
super(info);
}
@Override
public Statement createStatement() throws SQLException {
return new MockStatement(false);
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return new MockStatement(sql.equals(superSlowSql));
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
return new MockStatement(sql.equals(superSlowSql));
}
}
public static class MockStatement extends org.apache.tomcat.jdbc.test.driver.Statement {
boolean slow = false;
public MockStatement(boolean slow) {
this.slow = slow;
}
@Override
public boolean execute(String sql) throws SQLException {
if (failedSql.equals(sql)) {
throw new SQLException("Invalid SQL:"+sql);
}
if (slow || superSlowSql.equals(sql)) {
try {
Thread.sleep(200);
}catch (Exception x) {
}
}
return super.execute(sql);
}
@Override
public ResultSet executeQuery(String sql) throws SQLException {
if (failedSql.equals(sql)) {
throw new SQLException("Invalid SQL:"+sql);
}
if (slow || superSlowSql.equals(sql)) {
try {
Thread.sleep(200);
}catch (Exception x) {
}
}
return super.executeQuery(sql);
}
@Override
public ResultSet executeQuery() throws SQLException {
if (slow) {
try {
Thread.sleep(200);
}catch (Exception x) {
}
}
return super.executeQuery();
}
}
}

View File

@@ -0,0 +1,209 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
import org.apache.tomcat.jdbc.pool.interceptor.StatementCache;
import org.apache.tomcat.jdbc.pool.interceptor.StatementCounterInterceptor;
public class TestStatementCache extends DefaultTestCase {
private static volatile TestStatementCacheInterceptor interceptor = null;
@Override
@After
public void tearDown() throws Exception {
interceptor = null;
super.tearDown();
}
private void config(boolean cachePrepared, boolean cacheCallable, int max) {
datasource.getPoolProperties().setJdbcInterceptors(TestStatementCacheInterceptor.class.getName()+
"(prepared="+cachePrepared+",callable="+cacheCallable+",max="+max+")");
}
@Test
public void testIsCacheEnabled() throws Exception {
config(true,true,50);
datasource.getConnection().close();
Assert.assertNotNull("Interceptor was not created.", interceptor);
}
@Test
public void testCacheProperties() throws Exception {
config(true,true,50);
datasource.getConnection().close();
Assert.assertTrue(interceptor.isCacheCallable());
Assert.assertTrue(interceptor.isCachePrepared());
Assert.assertEquals(50,interceptor.getMaxCacheSize());
}
@Test
public void testCacheProperties2() throws Exception {
config(false,false,100);
datasource.getConnection().close();
Assert.assertFalse(interceptor.isCacheCallable());
Assert.assertFalse(interceptor.isCachePrepared());
Assert.assertEquals(100,interceptor.getMaxCacheSize());
}
@Test
public void testPreparedStatementCache() throws Exception {
config(true,false,100);
Connection con = datasource.getConnection();
PreparedStatement ps1 = con.prepareStatement("select 1");
PreparedStatement ps2 = con.prepareStatement("select 1");
Assert.assertEquals(0,interceptor.getCacheSize().get());
ps1.close();
Assert.assertTrue(ps1.isClosed());
Assert.assertEquals(1,interceptor.getCacheSize().get());
PreparedStatement ps3 = con.prepareStatement("select 1");
Assert.assertEquals(0,interceptor.getCacheSize().get());
ps2.close();
Assert.assertTrue(ps2.isClosed());
ps3.close();
Assert.assertTrue(ps3.isClosed());
Assert.assertEquals(1,interceptor.getCacheSize().get());
con.close();
}
@Test
public void testPreparedStatementCache2() throws Exception {
init();
config(false,false,100);
Connection con = datasource.getConnection();
PreparedStatement ps1 = con.prepareStatement("select 1");
PreparedStatement ps2 = con.prepareStatement("select 1");
Assert.assertEquals(0,interceptor.getCacheSize().get());
ps1.close();
Assert.assertTrue(ps1.isClosed());
Assert.assertEquals(0,interceptor.getCacheSize().get());
PreparedStatement ps3 = con.prepareStatement("select 1");
Assert.assertEquals(0,interceptor.getCacheSize().get());
ps2.close();
Assert.assertTrue(ps2.isClosed());
ps3.close();
Assert.assertTrue(ps3.isClosed());
Assert.assertEquals(0,interceptor.getCacheSize().get());
con.close();
}
@Test
public void testStatementClose1() throws Exception {
init();
datasource.setJdbcInterceptors(
TestStatementCacheInterceptor.class.getName()
+ "(prepared=true,callable=false,max=1);"
+ StatementCounterInterceptor.class.getName());
Connection con = datasource.getConnection();
StatementCounterInterceptor counter = findInterceptor(con, StatementCounterInterceptor.class);
PreparedStatement ps1, ps2;
ps1 = con.prepareStatement("select 1");
Assert.assertEquals(1, counter.getActiveCount());
ps1.close();
Assert.assertEquals("Statement goes into cache, not closed", 1, counter.getActiveCount());
ps1 = con.prepareStatement("select 1");
Assert.assertEquals("Reusing statement from cache", 1, counter.getActiveCount());
ps2 = con.prepareStatement("select 1");
Assert.assertEquals("Reusing statement from cache", 2, counter.getActiveCount());
ps2.close();
Assert.assertEquals("Statement goes into cache, not closed", 2, counter.getActiveCount());
ps1.close();
// Cache has "max=1". The following tests BZ 54732.
Assert.assertEquals("Statement does not go into cache, closed", 1, counter.getActiveCount());
con.close();
Assert.assertEquals("Connection returned to the pool. Statement is in cache", 1, counter.getActiveCount());
datasource.close();
Assert.assertEquals("Pool cleared. All statements in cache are closed", 0, counter.getActiveCount());
}
@Test
public void testStatementClose2() throws Exception {
init();
datasource.setJdbcInterceptors(
TestStatementCacheInterceptor.class.getName()
+ "(prepared=false,callable=false,max=10);"
+ StatementCounterInterceptor.class.getName());
Connection con = datasource.getConnection();
StatementCounterInterceptor counter = findInterceptor(con, StatementCounterInterceptor.class);
PreparedStatement ps1 = con.prepareStatement("select 1");
Assert.assertEquals(1, counter.getActiveCount());
ps1.close();
Assert.assertEquals("Statement is not pooled, closes immediately", 0, counter.getActiveCount());
}
@Test
public void testMaxCacheSize() throws Exception {
init();
config(true,false,100);
Connection con1 = datasource.getConnection();
Connection con2 = datasource.getConnection();
for (int i=0; i<120; i++) {
Connection con = (i%2==0)?con1:con2;
PreparedStatement ps = con.prepareStatement("select "+i);
ps.close();
}
Assert.assertEquals(100,interceptor.getCacheSize().get());
con1.close();
con2.close();
}
public static class TestStatementCacheInterceptor extends StatementCache {
public TestStatementCacheInterceptor() {
TestStatementCache.interceptor = this;
}
}
/**
* Helper method that finds interceptor instance in interceptor chain of a
* proxied class.
*
* @param proxy
* Proxy class
* @param clazz
* Interceptor class that we are looking for
* @return Instance of <code>clazz</code>
*/
private static <T extends JdbcInterceptor> T findInterceptor(Object proxy,
Class<T> clazz) {
JdbcInterceptor interceptor = (JdbcInterceptor) Proxy
.getInvocationHandler(proxy);
while (interceptor != null) {
if (clazz.isInstance(interceptor)) {
return clazz.cast(interceptor);
}
interceptor = interceptor.getNext();
}
return null;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.PooledConnection;
public class TestSuspectTimeout extends DefaultTestCase {
@Test
public void testSuspect() throws Exception {
this.datasource.setMaxActive(100);
this.datasource.setMaxIdle(100);
this.datasource.setInitialSize(0);
this.datasource.getPoolProperties().setAbandonWhenPercentageFull(0);
this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(100);
this.datasource.getPoolProperties().setRemoveAbandoned(true);
this.datasource.getPoolProperties().setRemoveAbandonedTimeout(100);
this.datasource.getPoolProperties().setSuspectTimeout(1);
this.datasource.getPoolProperties().setLogAbandoned(true);
Connection con = datasource.getConnection();
Assert.assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
Thread.sleep(3000);
PooledConnection pcon = con.unwrap(PooledConnection.class);
Assert.assertTrue("Connection should be marked suspect",pcon.isSuspect());
con.close();
}
}

View File

@@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.util.HashSet;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
public class TestTimeout extends DefaultTestCase {
@Test
public void testCheckoutTimeout() throws Exception {
Set<Connection> cons = new HashSet<>();
try {
this.datasource.getPoolProperties().setTestWhileIdle(true);
this.datasource.getPoolProperties().setTestOnBorrow(false);
this.datasource.getPoolProperties().setTestOnReturn(false);
this.datasource.getPoolProperties().setValidationInterval(30000);
this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(1000);
this.datasource.getPoolProperties().setMaxActive(20);
this.datasource.getPoolProperties().setMaxWait(3000);
this.datasource.getPoolProperties().setRemoveAbandonedTimeout(5);
this.datasource.getPoolProperties().setMinEvictableIdleTimeMillis(5000);
this.datasource.getPoolProperties().setMinIdle(5);
this.datasource.getPoolProperties().setLogAbandoned(true);
System.out.println("About to test connection pool:"+datasource);
for (int i = 0; i < 21; i++) {
long now = System.currentTimeMillis();
cons.add(this.datasource.getConnection());
long delta = System.currentTimeMillis()-now;
System.out.println("Got connection #"+i+" in "+delta+" ms.");
}
Assert.fail();
} catch ( Exception x ) {
// Expected on 21st checkout
}finally {
Thread.sleep(2000);
}
for (Connection c : cons) {
c.close();
}
}
@Test
public void testCheckoutTimeoutFair() throws Exception {
Set<Connection> cons = new HashSet<>();
try {
this.datasource.getPoolProperties().setFairQueue(true);
this.datasource.getPoolProperties().setTestWhileIdle(true);
this.datasource.getPoolProperties().setTestOnBorrow(false);
this.datasource.getPoolProperties().setTestOnReturn(false);
this.datasource.getPoolProperties().setValidationInterval(30000);
this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(1000);
this.datasource.getPoolProperties().setMaxActive(20);
this.datasource.getPoolProperties().setMaxWait(3000);
this.datasource.getPoolProperties().setRemoveAbandonedTimeout(5);
this.datasource.getPoolProperties().setMinEvictableIdleTimeMillis(5000);
this.datasource.getPoolProperties().setMinIdle(5);
this.datasource.getPoolProperties().setLogAbandoned(true);
System.out.println("About to test connection pool:"+datasource);
for (int i = 0; i < 21; i++) {
long now = System.currentTimeMillis();
cons.add(this.datasource.getConnection());
long delta = System.currentTimeMillis()-now;
System.out.println("Got connection #"+i+" in "+delta+" ms.");
}
Assert.fail();
} catch ( Exception x ) {
// Expected on 21st checkout
}finally {
Thread.sleep(2000);
}
for (Connection c : cons) {
c.close();
}
}
}

View File

@@ -0,0 +1,650 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.Properties;
import java.util.logging.Logger;
import javax.sql.PooledConnection;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class TestValidation extends DefaultTestCase {
public static final Boolean WITHAUTOCOMMIT = Boolean.TRUE;
public static final Boolean NOAUTOCOMMIT = Boolean.FALSE;
public static final String WITHVALIDATIONQUERY = "SELECT 1";
public static final String NOVALIDATIONQUERY = null;
@Before
public void setUp() throws SQLException {
DriverManager.registerDriver(new MockDriver());
// use our mock driver
datasource.setDriverClassName(MockDriver.class.getName());
datasource.setUrl(MockDriver.getUrlWithValidationOutcomes(ValidationOutcome.SUCCESS));
// Required to trigger validation query's execution
datasource.setInitialSize(1);
datasource.setMinIdle(1);
datasource.setMaxIdle(1);
datasource.setMaxActive(2);
// Validation interval is disabled by default to ensure validation occurs everytime
datasource.setValidationInterval(-1);
// No validation query by default
datasource.setValidationQuery(null);
}
@After
public void cleanup() throws SQLException {
datasource = createDefaultDataSource();
DriverManager.deregisterDriver(new MockDriver());
}
private PooledConnection getPooledConnection() throws SQLException {
return (PooledConnection) datasource.getConnection();
}
private static MockConnection getMock(PooledConnection pooledConnection) throws SQLException {
return (MockConnection) pooledConnection.getConnection();
}
/* -------------------------------- *
* Validation onConnect *
* -------------------------------- */
private void checkOnConnectValidationWithOutcome(ValidationOutcome validationOutcomes, String validationQuery, Boolean autoCommit) throws SQLException {
datasource.setUrl(MockDriver.getUrlWithValidationOutcomes(validationOutcomes));
datasource.getPoolProperties().setTestOnConnect(true);
datasource.getPoolProperties().setValidationQuery(validationQuery);
datasource.getPoolProperties().setDefaultAutoCommit(autoCommit);
PooledConnection cxn1 = getPooledConnection();
MockConnection mockCxn1 = getMock(cxn1);
Assert.assertFalse("No transaction must be running after connection is obtained", mockCxn1.isRunningTransaction());
}
/* ------- No validation query ----- */
@Test
public void testOnConnectValidationWithoutValidationQueryDoesNotOccurWhenDisabled() throws SQLException {
datasource.setUrl(MockDriver.getUrlWithValidationOutcomes(ValidationOutcome.FAILURE));
datasource.getPoolProperties().setTestOnConnect(false);
datasource.getPoolProperties().setDefaultAutoCommit(Boolean.FALSE);
PooledConnection cxn = getPooledConnection();
Assert.assertFalse("No transaction must be running after connection is obtained", getMock(cxn).isRunningTransaction());
}
@Test
public void testOnConnectValidationSuccessWithoutValidationQueryAndAutoCommitEnabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.SUCCESS, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test(expected=SQLException.class)
public void testOnConnectFailureThenSuccessWithoutValidationQueryAndAutoCommitEnabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.FAILURE, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test(expected=SQLException.class)
public void testOnConnectExceptionThenSuccessWithoutValidationQueryAndAutoCommitEnabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.EXCEPTION, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnConnectValidationSuccessWithoutValidationQueryAndAutoCommitDisabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.SUCCESS, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test(expected=SQLException.class)
public void testOnConnectFailureThenSuccessWithoutValidationQueryAndAutoCommitDisabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.FAILURE, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test(expected=SQLException.class)
public void testOnConnectExceptionThenSuccessWithoutValidationQueryAndAutoCommitDisabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.EXCEPTION, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
/* ------- With validation query ----- */
@Test
public void testOnConnectValidationWithValidationSQLDoesNotOccurWhenDisabled() throws SQLException {
this.datasource.setUrl(MockDriver.getUrlWithValidationOutcomes(ValidationOutcome.FAILURE));
datasource.getPoolProperties().setTestOnConnect(false);
datasource.getPoolProperties().setDefaultAutoCommit(Boolean.FALSE);
datasource.getPoolProperties().setValidationQuery("SELECT 1");
PooledConnection cxn = getPooledConnection();
Assert.assertFalse("No transaction must be running after connection is obtained", getMock(cxn).isRunningTransaction());
}
@Test
public void testOnConnectValidationSuccessWithValidationQueryAndAutoCommitEnabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.SUCCESS, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test(expected=SQLException.class)
public void testOnConnectFailureThenSuccessWithValidationQueryAndAutoCommitEnabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.FAILURE, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test(expected=SQLException.class)
public void testOnConnectExceptionThenSuccessWithValidationQueryAndAutoCommitEnabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.EXCEPTION, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnConnectValidationSuccessWithValidationQueryAndAutoCommitDisabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.SUCCESS, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test(expected=SQLException.class)
public void testOnConnectFailureThenSuccessWithValidationQueryAndAutoCommitDisabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.FAILURE, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test(expected=SQLException.class)
public void testOnConnectExceptionThenSuccessWithValidationQueryAndAutoCommitDisabled() throws SQLException {
checkOnConnectValidationWithOutcome(ValidationOutcome.EXCEPTION, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
/* -------------------------------- *
* Validation onBorrow *
* -------------------------------- */
private void obtainCxnWithValidationOutcomeAndAttemptAgain(ValidationOutcome validationOutcome, String validationQuery, Boolean autoCommit) throws SQLException {
datasource.getPoolProperties().setValidationQuery(validationQuery);
datasource.getPoolProperties().setDefaultAutoCommit(autoCommit);
PooledConnection cxn1 = getPooledConnection();
MockConnection mockCxn1 = getMock(cxn1);
Assert.assertFalse("No transaction must be running after connection is obtained", mockCxn1.isRunningTransaction());
// Discard connection and set next validation outcome to provided outcome value
mockCxn1.setValidationOutcome(validationOutcome);
cxn1.close();
PooledConnection cxn2 = getPooledConnection();
MockConnection mockCxn2 = getMock(cxn2);
Assert.assertFalse("No transaction must be running after connection is obtained", mockCxn2.isRunningTransaction());
if (validationOutcome == ValidationOutcome.SUCCESS) {
Assert.assertEquals("Connection with successful validation is reused", mockCxn1, mockCxn2);
}
else {
Assert.assertNotEquals("Connection with failed validation must not be returned again", mockCxn1, mockCxn2);
}
}
private void obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome validationOutcome, String validationQuery, Boolean autoCommit) throws SQLException {
datasource.getPoolProperties().setTestOnBorrow(true);
obtainCxnWithValidationOutcomeAndAttemptAgain(validationOutcome, validationQuery, autoCommit);
}
/* ------- No validation query ----- */
@Test
public void testOnBorrowValidationWithoutValidationQueryDoesNotOccurWhenDisabled() throws SQLException {
datasource.getPoolProperties().setTestOnBorrow(false);
datasource.getPoolProperties().setDefaultAutoCommit(Boolean.FALSE);
PooledConnection cxn = getPooledConnection();
Assert.assertFalse("No transaction must be running after connection is obtained", getMock(cxn).isRunningTransaction());
}
@Test
public void testOnBorrowValidationSuccessWithoutValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.SUCCESS, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnBorrowValidationFailureWithoutValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.FAILURE, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnBorrowValidationExceptionWithoutValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.EXCEPTION, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnBorrowValidationSuccessWithoutValidationQueryAndAutoCommitDisabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.SUCCESS, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testOnBorrowValidationFailureWithoutValidationQueryAndAutoCommitDisabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.FAILURE, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testOnBorrowValidationExceptionWithoutValidationQueryAndAutoCommitDisabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.EXCEPTION, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
/* ------- With validation query ----- */
@Test
public void testOnBorrowValidationWithValidationQueryDoesNotOccurWhenDisabled() throws SQLException {
datasource.getPoolProperties().setTestOnBorrow(false);
datasource.getPoolProperties().setDefaultAutoCommit(Boolean.FALSE);
datasource.getPoolProperties().setValidationQuery("SELECT 1");
PooledConnection cxn = getPooledConnection();
Assert.assertFalse("No transaction must be running after connection is obtained", getMock(cxn).isRunningTransaction());
}
@Test
public void testOnBorrowValidationSuccessWithValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.SUCCESS, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnBorrowValidationFailureWithValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.FAILURE, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnBorrowValidationExceptionWithValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.EXCEPTION, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnBorrowValidationSuccessWithValidationQueryAndAutoCommitDisabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.SUCCESS, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testOnBorrowValidationFailureWithValidationQueryAndAutoCommitDisabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.FAILURE, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testOnBorrowValidationExceptionWithValidationQueryAndAutoCommitDisabled() throws SQLException {
obtainCxnWithOnBorrowValidationOutcomeAndAttemptAgain(ValidationOutcome.EXCEPTION, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
/* -------------------------------- *
* Validation onReturn *
* -------------------------------- */
private void obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome validationOutcome, String validationQuery, Boolean autoCommit) throws SQLException {
datasource.getPoolProperties().setTestOnReturn(true);
obtainCxnWithValidationOutcomeAndAttemptAgain(validationOutcome, validationQuery, autoCommit);
}
/* ------- No validation query ----- */
@Test
public void testOnReturnValidationWithoutValidationQueryDoesNotOccurWhenDisabled() throws SQLException {
datasource.getPoolProperties().setTestOnReturn(false);
datasource.getPoolProperties().setDefaultAutoCommit(Boolean.FALSE);
PooledConnection cxn = getPooledConnection();
Assert.assertFalse("No transaction must be running after connection is obtained", getMock(cxn).isRunningTransaction());
}
@Test
public void testOnReturnValidationSuccessWithoutValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.SUCCESS, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnReturnValidationFailureWithoutValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.FAILURE, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnReturnValidationExceptionWithoutValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.EXCEPTION, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnReturnValidationSuccessWithoutValidationQueryAndAutoCommitDisabled() throws SQLException {
datasource.getPoolProperties().setTestOnReturn(true);
datasource.getPoolProperties().setDefaultAutoCommit(Boolean.FALSE);
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.SUCCESS, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testOnReturnValidationFailureWithoutValidationQueryAndAutoCommitDisabled() throws SQLException {
datasource.getPoolProperties().setTestOnReturn(true);
datasource.getPoolProperties().setDefaultAutoCommit(Boolean.FALSE);
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.FAILURE, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testOnReturnValidationExceptionWithoutValidationQueryAndAutoCommitDisabled() throws SQLException {
datasource.getPoolProperties().setTestOnReturn(true);
datasource.getPoolProperties().setDefaultAutoCommit(Boolean.FALSE);
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.EXCEPTION, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
/* ------- With validation query ----- */
@Test
public void testOnReturnValidationWithValidationQueryDoesNotOccurWhenDisabled() throws SQLException {
datasource.getPoolProperties().setTestOnReturn(false);
datasource.getPoolProperties().setDefaultAutoCommit(Boolean.FALSE);
datasource.getPoolProperties().setValidationQuery("SELECT 1");
PooledConnection cxn = getPooledConnection();
Assert.assertFalse("No transaction must be running after connection is obtained", getMock(cxn).isRunningTransaction());
}
@Test
public void testOnReturnValidationSuccessWithValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.SUCCESS, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnReturnValidationFailureWithValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.FAILURE, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnReturnValidationExceptionWithValidationQueryAndAutoCommitEnabled() throws SQLException {
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.EXCEPTION, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testOnReturnValidationSuccessWithValidationQueryAndAutoCommitDisabled() throws SQLException {
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.SUCCESS, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testOnReturnValidationFailureWithValidationQueryAndAutoCommitDisabled() throws SQLException {
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.FAILURE, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testOnReturnValidationExceptionWithValidationQueryAndAutoCommitDisabled() throws SQLException {
obtainCxnWithOnReturnValidationOutcomeAndAttemptAgain(ValidationOutcome.EXCEPTION, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
/* -------------------------------- *
* Validation whileIdle *
* -------------------------------- */
private void obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome validationOutcome, String validationQuery, Boolean autoCommit)
throws SQLException, InterruptedException {
datasource.getPoolProperties().setTestWhileIdle(true);
datasource.getPoolProperties().setValidationInterval(1);
datasource.getPoolProperties().setDefaultAutoCommit(autoCommit);
datasource.getPoolProperties().setValidationQuery(validationQuery);
datasource.setUrl(MockDriver.getUrlWithValidationOutcomes(validationOutcome));
PooledConnection cxn1 = getPooledConnection();
MockConnection mockCxn1 = getMock(cxn1);
Assert.assertFalse("No transaction must be running after connection is obtained", mockCxn1.isRunningTransaction());
cxn1.close();
Assert.assertEquals("Pool must contain 1 idle connection at this time", datasource.getIdle(), 1);
Thread.sleep(1200); // Nasty - instrument PooledConnection to drive time measurement instead of hard-coded System.currentTimeMillis()
datasource.testIdle();
if (validationOutcome == ValidationOutcome.SUCCESS) {
Assert.assertEquals("Pool must contain 1 idle connection at this time", datasource.getIdle(), 1);
}
else {
Assert.assertEquals("Pool must not contain any idle connection at this time", datasource.getIdle(), 0);
}
}
/* ------- No validation query ----- */
@Test
public void testWhileIdleReturnValidationWithoutValidationQueryDoesNotOccurWhenDisabled() throws SQLException, InterruptedException {
datasource.setUrl(MockDriver.getUrlWithValidationOutcomes(ValidationOutcome.FAILURE));
datasource.getPoolProperties().setTestWhileIdle(false);
datasource.getPoolProperties().setDefaultAutoCommit(Boolean.FALSE);
datasource.getPoolProperties().setValidationInterval(1);
PooledConnection cxn = getPooledConnection();
cxn.close();
Assert.assertEquals("Pool must contain 1 idle connection at this time", datasource.getIdle(), 1);
Thread.sleep(1200); // Nasty - instrument PooledConnection to drive time measurement instead of hard-coded System.currentTimeMillis()
datasource.testIdle();
Assert.assertEquals("Pool must contain 1 idle connection at this time", datasource.getIdle(), 1);
}
@Test
public void testWhileIdleValidationSuccessWithoutValidationQueryAndAutoCommitEnabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.SUCCESS, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testWhileIdleValidationFailureWithoutValidationQueryAndAutoCommitEnabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.FAILURE, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testWhileIdleValidationExceptionWithoutValidationQueryAndAutoCommitEnabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.EXCEPTION, NOVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testWhileIdleValidationSuccessWithoutValidationQueryAndAutoCommitDisabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.SUCCESS, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testWhileIdleValidationFailureWithoutValidationQueryAndAutoCommitDisabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.FAILURE, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testWhileIdleValidationExceptionWithoutValidationQueryAndAutoCommitDisabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.EXCEPTION, NOVALIDATIONQUERY, NOAUTOCOMMIT);
}
/* ------- With validation query ----- */
@Test
public void testWhileIdleValidationSuccessWithValidationQueryAndAutoCommitEnabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.SUCCESS, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testWhileIdleValidationFailureWithValidationQueryAndAutoCommitEnabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.FAILURE, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testWhileIdleValidationExceptionWithValidationQueryAndAutoCommitEnabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.EXCEPTION, WITHVALIDATIONQUERY, WITHAUTOCOMMIT);
}
@Test
public void testWhileIdleValidationSuccessWithValidationQueryAndAutoCommitDisabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.SUCCESS, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testWhileIdleValidationFailureWithValidationQueryAndAutoCommitDisabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.FAILURE, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
@Test
public void testWhileIdleValidationExceptionWithValidationQueryAndAutoCommitDisabled() throws SQLException, InterruptedException {
obtainThenReleaseCxnAndAssessIdleValidationWithOutcome(ValidationOutcome.EXCEPTION, WITHVALIDATIONQUERY, NOAUTOCOMMIT);
}
/* ------- Helper mock classes ----- */
public static enum ValidationOutcome {
SUCCESS, // Validation returns true
FAILURE, // Validation returns false
EXCEPTION // Validation throws an unexpected exception
}
/**
* Mock Driver, Connection and Statement implementations used to control validation outcome and verify transaction status.
*/
public static class MockDriver implements java.sql.Driver {
public static final String url = "jdbc:tomcat:mock";
public static String getUrlWithValidationOutcomes(ValidationOutcome validationOutcome) {
return url + "?" + validationOutcome;
}
private ValidationOutcome getValidationOutcomeFromUrl(String url) {
String outcomesAsString = url.substring(url.lastIndexOf("?")+1);
return ValidationOutcome.valueOf(outcomesAsString);
}
public MockDriver() {
}
@Override
public boolean acceptsURL(String url) throws SQLException {
return url!=null && url.startsWith(MockDriver.url);
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
return new MockConnection(info, getValidationOutcomeFromUrl(url));
}
@Override
public int getMajorVersion() {
return 0;
}
@Override
public int getMinorVersion() {
return 0;
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
return null;
}
@Override
public boolean jdbcCompliant() {
return false;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
public static class MockConnection extends org.apache.tomcat.jdbc.test.driver.Connection {
private boolean autoCommit = false;
private boolean runningTransaction = false;
private ValidationOutcome validationOutcome;
public MockConnection(Properties info, ValidationOutcome validationOutcome) {
super(info);
this.validationOutcome = validationOutcome;
}
public boolean isRunningTransaction() {
return runningTransaction;
}
protected void statementExecuted() {
this.runningTransaction = !autoCommit;
}
protected void transactionCleared() {
this.runningTransaction = false;
}
protected void setValidationOutcome(ValidationOutcome validationOutcome) {
this.validationOutcome = validationOutcome;
}
protected ValidationOutcome getValidationOutcome() {
return validationOutcome;
}
@Override
public boolean getAutoCommit() throws SQLException {
return autoCommit;
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
this.autoCommit = autoCommit;
}
@Override
public Statement createStatement() throws SQLException {
return new MockStatement(this);
}
@Override
public void commit() throws SQLException {
super.commit();
transactionCleared();
}
@Override
public void rollback() throws SQLException {
super.rollback();
transactionCleared();
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
super.rollback(savepoint);
transactionCleared();
}
@Override
public boolean isValid(int timeout) throws SQLException {
statementExecuted();
switch (validationOutcome) {
case SUCCESS: { return true; }
case FAILURE: { return false; }
case EXCEPTION: { throw new SQLException("Unexpected error generated in test"); }
default: { return true; }
}
}
}
public static class MockStatement extends org.apache.tomcat.jdbc.test.driver.Statement {
private MockConnection connection;
public MockStatement(MockConnection connection) {
super();
this.connection = connection;
}
@Override
public boolean execute(String sql) throws SQLException {
if (connection.getValidationOutcome()==ValidationOutcome.SUCCESS) {
return false;
}
else {
throw new SQLException("Simulated validation query failure");
}
}
}
}

View File

@@ -0,0 +1,260 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLTimeoutException;
import java.sql.Statement;
import java.util.Properties;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor;
public class TestValidationQueryTimeout extends DefaultTestCase {
private static final int TIMEOUT = 10;
private static boolean isTimeoutSet;
private static final String longQuery = "select * from test as A, test as B, test as C, test as D, test as E";
@Before
public void setUp() throws SQLException {
DriverManager.registerDriver(new MockDriver());
// use our mock driver
this.datasource.setDriverClassName(MockDriver.class.getName());
this.datasource.setUrl(MockDriver.url);
// Required to trigger validation query's execution
this.datasource.setInitialSize(1);
this.datasource.setTestOnBorrow(true);
this.datasource.setValidationInterval(-1);
this.datasource.setValidationQuery("SELECT 1");
this.datasource.setValidationQueryTimeout(TIMEOUT);
isTimeoutSet = false;
}
@Override
@After
public void tearDown() throws SQLException {
DriverManager.deregisterDriver(new MockDriver());
}
@Test
public void testValidationQueryTimeoutEnabled() throws Exception {
// because testOnBorrow is true, this triggers the validation query
Connection con = this.datasource.getConnection();
Assert.assertTrue(isTimeoutSet);
con.close();
}
@Test
public void testValidationQueryTimeoutDisabled() throws Exception {
this.datasource.setValidationQueryTimeout(-1);
// because testOnBorrow is true, this triggers the validation query
Connection con = this.datasource.getConnection();
Assert.assertFalse(isTimeoutSet);
con.close();
}
@Test
public void testValidationQueryTimeoutWithQueryTimeoutInterceptor() throws Exception {
int interceptorTimeout = 30;
this.datasource.setJdbcInterceptors(
QueryTimeoutInterceptor.class.getName()+
"(queryTimeout="+ interceptorTimeout +")");
// because testOnBorrow is true, this triggers the validation query
Connection con = this.datasource.getConnection();
Assert.assertTrue(isTimeoutSet);
// now create a statement, make sure the query timeout is set by the interceptor
Statement st = con.createStatement();
Assert.assertEquals(interceptorTimeout, st.getQueryTimeout());
st.close();
st = con.prepareStatement("");
Assert.assertEquals(interceptorTimeout, st.getQueryTimeout());
st.close();
st = con.prepareCall("");
Assert.assertEquals(interceptorTimeout, st.getQueryTimeout());
st.close();
con.close();
// pull another connection and check it
isTimeoutSet = false;
Connection con2 = this.datasource.getConnection();
Assert.assertTrue(isTimeoutSet);
con2.close();
}
// this test depends on the execution time of the validation query
// specifically, it needs to run for longer than 1 second to pass
// if this fails
@Test(expected=SQLException.class)
public void testValidationQueryTimeoutOnConnection() throws Exception {
// use our mock driver
// Required to trigger validation query's execution
this.datasource.setTestOnConnect(true);
this.datasource.setValidationInterval(-1);
this.datasource.setValidationQuery(longQuery);
this.datasource.setValidationQueryTimeout(1);
this.datasource.getConnection();
}
@Test(expected=SQLException.class)
public void testValidationInvalidOnConnection() throws Exception {
// use a real driver cause we have an invalid query to validate
this.datasource.setDriverClassName("org.h2.Driver");
this.datasource.setUrl("jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=FALSE");
// Required to trigger validation query's execution
this.datasource.setTestOnBorrow(true);
this.datasource.setInitialSize(1);
this.datasource.setTestOnConnect(true);
this.datasource.setValidationInterval(-1);
this.datasource.setValidationQuery("SELECT");
this.datasource.setValidationQueryTimeout(1);
this.datasource.getConnection();
}
@Test
public void testLongValidationQueryTime() throws Exception {
// use our mock driver
Connection con = this.datasource.getConnection();
Statement stmt = null;
long start = 0, end = 0;
try {
stmt = con.createStatement();
// set the query timeout to 2 sec
// this keeps this test from slowing things down too much
stmt.setQueryTimeout(2);
// assert that our long query takes longer than one second to run
// this is a requirement for other tests to run properly
start = System.currentTimeMillis();
stmt.execute(longQuery);
} catch (SQLTimeoutException ex) {
} catch (SQLException x) {
Assert.fail("We should have got a timeout exception.");
} finally {
end = System.currentTimeMillis();
if (stmt != null) { stmt.close(); }
if (con != null) { con.close(); }
Assert.assertTrue(start != 0 && end != 0);
//we're faking it
//Assert.assertTrue((end - start) > 1000);
}
}
@Test(expected = SQLException.class)
public void testValidationQueryTimeoutOnBorrow() throws Exception {
// Required to trigger validation query's execution
this.datasource.setTestOnBorrow(true);
this.datasource.setValidationInterval(-1);
this.datasource.setValidationQuery(longQuery);
this.datasource.setValidationQueryTimeout(1);
// assert that even though the validation query we don't get a connection
this.datasource.getConnection();
}
/**
* Mock Driver, Connection and Statement implementations use to verify setQueryTimeout was called.
*/
public static class MockDriver implements java.sql.Driver {
public static final String url = "jdbc:tomcat:mock";
public MockDriver() {
}
@Override
public boolean acceptsURL(String url) throws SQLException {
return url!=null && url.equals(MockDriver.url);
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
return new MockConnection(info);
}
@Override
public int getMajorVersion() {
return 0;
}
@Override
public int getMinorVersion() {
return 0;
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
return null;
}
@Override
public boolean jdbcCompliant() {
return false;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
public static class MockConnection extends org.apache.tomcat.jdbc.test.driver.Connection {
public MockConnection(Properties info) {
super(info);
}
@Override
public Statement createStatement() throws SQLException {
return new MockStatement();
}
}
public static class MockStatement extends org.apache.tomcat.jdbc.test.driver.Statement {
@Override
public void setQueryTimeout(int seconds) throws SQLException {
super.setQueryTimeout(seconds);
isTimeoutSet = true;
}
@Override
public boolean execute(String sql) throws SQLException {
if (longQuery.equals(sql)) {
throw new SQLTimeoutException();
} else {
return super.execute(sql);
}
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test;
import java.sql.Connection;
import org.junit.Assert;
import org.junit.Test;
public class TwoDataSources extends DefaultTestCase {
@Test
public void testTwoDataSources() throws Exception {
org.apache.tomcat.jdbc.pool.DataSource d1 = this.createDefaultDataSource();
org.apache.tomcat.jdbc.pool.DataSource d2 = this.createDefaultDataSource();
d1.setRemoveAbandoned(true);
d1.setRemoveAbandonedTimeout(2);
d1.setTimeBetweenEvictionRunsMillis(1000);
d2.setRemoveAbandoned(false);
Connection c1 = d1.getConnection();
Connection c2 = d2.getConnection();
Thread.sleep(5000);
try {
c1.createStatement();
Assert.assertTrue("Connection should have been abandoned.",false);
}catch (Exception x) {
Assert.assertTrue("This is correct, c1 is abandoned",true);
}
try {
c2.createStatement();
Assert.assertTrue("Connection should not have been abandoned.",true);
}catch (Exception x) {
Assert.assertTrue("Connection c2 should be working",false);
}
try {
Assert.assertTrue("Connection should have been closed.",c1.isClosed());
}catch (Exception x) {
Assert.assertTrue("This is correct, c1 is closed",true);
}
try {
Assert.assertFalse("Connection c2 should not have been closed.",c2.isClosed());
}catch (Exception x) {
Assert.assertTrue("Connection c2 should be working",false);
}
}
}

View File

@@ -0,0 +1,320 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test.driver;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import org.apache.tomcat.jdbc.pool.PooledConnection;
public class Connection implements java.sql.Connection {
Properties info;
public Connection(Properties info) {
this.info = info;
}
public String getUsername() {
return info.getProperty(PooledConnection.PROP_USER);
}
public String getPassword() {
return info.getProperty(PooledConnection.PROP_PASSWORD);
}
@Override
public void clearWarnings() throws SQLException {
}
@Override
public void close() throws SQLException {
Driver.disconnectCount.incrementAndGet();
}
@Override
public void commit() throws SQLException {
}
@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return null;
}
@Override
public Blob createBlob() throws SQLException {
return null;
}
@Override
public Clob createClob() throws SQLException {
return null;
}
@Override
public NClob createNClob() throws SQLException {
return null;
}
@Override
public SQLXML createSQLXML() throws SQLException {
return null;
}
@Override
public Statement createStatement() throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return null;
}
@Override
public boolean getAutoCommit() throws SQLException {
return false;
}
@Override
public String getCatalog() throws SQLException {
return null;
}
@Override
public Properties getClientInfo() throws SQLException {
return null;
}
@Override
public String getClientInfo(String name) throws SQLException {
return null;
}
@Override
public int getHoldability() throws SQLException {
return 0;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
return null;
}
@Override
public int getTransactionIsolation() throws SQLException {
return 0;
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return null;
}
@Override
public SQLWarning getWarnings() throws SQLException {
return null;
}
@Override
public boolean isClosed() throws SQLException {
return false;
}
@Override
public boolean isReadOnly() throws SQLException {
return false;
}
@Override
public boolean isValid(int timeout) throws SQLException {
return false;
}
@Override
public String nativeSQL(String sql) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
}
@Override
public void rollback() throws SQLException {
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
}
@Override
public void setCatalog(String catalog) throws SQLException {
}
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
}
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
}
@Override
public void setHoldability(int holdability) throws SQLException {
}
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
}
@Override
public Savepoint setSavepoint() throws SQLException {
return null;
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
return null;
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
}
@Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public void setSchema(String schema) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public String getSchema() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void abort(Executor executor) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getNetworkTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
}

View File

@@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.test.driver;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
public class Driver implements java.sql.Driver {
public static final String url = "jdbc:tomcat:test";
public static final AtomicInteger connectCount = new AtomicInteger(0);
public static final AtomicInteger disconnectCount = new AtomicInteger(0);
public static void reset() {
connectCount.set(0);
disconnectCount.set(0);
}
static {
try {
DriverManager.registerDriver(new Driver());
}catch (Exception x) {
x.printStackTrace();
throw new RuntimeException(x);
}
}
public Driver() {
}
@Override
public boolean acceptsURL(String url) throws SQLException {
return url!=null && url.equals(Driver.url);
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
connectCount.addAndGet(1);
return new org.apache.tomcat.jdbc.test.driver.Connection(info);
}
@Override
public int getMajorVersion() {
return 0;
}
@Override
public int getMinorVersion() {
return 0;
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
return null;
}
@Override
public boolean jdbcCompliant() {
return false;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More