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
File diff suppressed because it is too large
Load Diff
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
File diff suppressed because it is too large
Load Diff
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);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user