init
This commit is contained in:
201
modules/jdbc-pool/LICENSE
Normal file
201
modules/jdbc-pool/LICENSE
Normal 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
6
modules/jdbc-pool/NOTICE
Normal 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/).
|
||||
|
||||
102
modules/jdbc-pool/build.properties.default
Normal file
102
modules/jdbc-pool/build.properties.default
Normal 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
529
modules/jdbc-pool/build.xml
Normal 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 &#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>
|
||||
|
||||
133
modules/jdbc-pool/doc/changelog.xml
Normal file
133
modules/jdbc-pool/doc/changelog.xml
Normal 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'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>
|
||||
988
modules/jdbc-pool/doc/jdbc-pool.xml
Normal file
988
modules/jdbc-pool/doc/jdbc-pool.xml
Normal 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<Connection></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="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
|
||||
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
|
||||
</code>
|
||||
<br/>
|
||||
is the same as
|
||||
<br/>
|
||||
<code> jdbcInterceptors="ConnectionState;StatementFinalizer"</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="ConnectionState;StatementFinalizer(useEquals=true)"
|
||||
</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<Connection> 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>"close".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>
|
||||
249
modules/jdbc-pool/doc/package.xsl
Normal file
249
modules/jdbc-pool/doc/package.xsl
Normal 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&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>
|
||||
31
modules/jdbc-pool/doc/project.xml
Normal file
31
modules/jdbc-pool/doc/project.xml
Normal 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
158
modules/jdbc-pool/pom.xml
Normal 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>
|
||||
22
modules/jdbc-pool/resources/MANIFEST.MF
Normal file
22
modules/jdbc-pool/resources/MANIFEST.MF
Normal 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"
|
||||
@@ -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>
|
||||
* <Resource factory="org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory"
|
||||
* name="jdbc/test"
|
||||
* type="org.apache.derby.jdbc.ClientXADataSource"
|
||||
* databaseName="sample"
|
||||
* createDatabase="create"
|
||||
* serverName="localhost"
|
||||
* port="1527"/>
|
||||
* </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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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()>0;
|
||||
result = result && (isRemoveAbandoned() && getRemoveAbandonedTimeout()>0);
|
||||
result = result || (isTestWhileIdle() && 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) > 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) > 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();
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 > 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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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")+"]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 "batch" 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 "batch" 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 "batch" 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
//=================================================================
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user