pax_global_header00006660000000000000000000000064140542513220014510gustar00rootroot0000000000000052 comment=48c289d95c2374ee11e3276a8bcb93b7f99015be commons-pool-rel-commons-pool-2.10.0/000077500000000000000000000000001405425132200174125ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/.github/000077500000000000000000000000001405425132200207525ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/.github/dependabot.yml000066400000000000000000000017341405425132200236070ustar00rootroot00000000000000# 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. version: 2 updates: - package-ecosystem: "maven" directory: "/" schedule: interval: "daily" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" commons-pool-rel-commons-pool-2.10.0/.github/workflows/000077500000000000000000000000001405425132200230075ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/.github/workflows/maven.yml000066400000000000000000000031421405425132200246400ustar00rootroot00000000000000# 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. name: Java CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental }} strategy: matrix: java: [ 8, 11, 16 ] experimental: [false] include: - java: 17-ea experimental: true steps: - uses: actions/checkout@v2.3.4 - uses: actions/cache@v2.1.6 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v2 with: distribution: 'adopt' java-version: ${{ matrix.java }} - name: Build with Maven run: mvn -V -B --file pom.xml --no-transfer-progress commons-pool-rel-commons-pool-2.10.0/.gitignore000066400000000000000000000001111405425132200213730ustar00rootroot00000000000000target site-content /.classpath /.project /.settings/ /bin/ .idea/ *.iml commons-pool-rel-commons-pool-2.10.0/.travis.yml000066400000000000000000000021621405425132200215240ustar00rootroot00000000000000# 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. language: java cache: directories: - $HOME/.m2 jdk: - openjdk8 - openjdk11 - openjdk15 - openjdk-ea matrix: allow_failures: - jdk: openjdk-ea script: - mvn -V --no-transfer-progress after_success: - mvn -V --no-transfer-progress clean cobertura:cobertura coveralls:report -Ptravis-cobertura commons-pool-rel-commons-pool-2.10.0/CONTRIBUTING.md000066400000000000000000000146011405425132200216450ustar00rootroot00000000000000 Contributing to Apache Commons Pool ====================== You have found a bug or you have an idea for a cool new feature? Contributing code is a great way to give something back to the open source community. Before you dig right into the code there are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. Getting Started --------------- + Make sure you have a [JIRA account](https://issues.apache.org/jira/). + Make sure you have a [GitHub account](https://github.com/signup/free). + If you're planning to implement a new feature it makes sense to discuss your changes on the [dev list](https://commons.apache.org/mail-lists.html) first. This way you can make sure you're not wasting your time on something that isn't considered to be in Apache Commons Pool's scope. + Submit a [Jira Ticket][jira] for your issue, assuming one does not already exist. + Clearly describe the issue including steps to reproduce when it is a bug. + Make sure you fill in the earliest version that you know has the issue. + Find the corresponding [repository on GitHub](https://github.com/apache/?query=commons-), [fork](https://help.github.com/articles/fork-a-repo/) and check out your forked repository. Making Changes -------------- + Create a _topic branch_ for your isolated work. * Usually you should base your branch on the `master` or `trunk` branch. * A good topic branch name can be the JIRA bug id plus a keyword, e.g. `POOL-123-InputStream`. * If you have submitted multiple JIRA issues, try to maintain separate branches and pull requests. + Make commits of logical units. * Make sure your commit messages are meaningful and in the proper format. Your commit message should contain the key of the JIRA issue. * e.g. `POOL-123: Close input stream earlier` + Respect the original code style: + Only use spaces for indentation. + Create minimal diffs - disable _On Save_ actions like _Reformat Source Code_ or _Organize Imports_. If you feel the source code should be reformatted create a separate PR for this change first. + Check for unnecessary whitespace with `git diff` -- check before committing. + Make sure you have added the necessary tests for your changes, typically in `src/test/java`. + Run all the tests with `mvn clean verify` to assure nothing else was accidentally broken. Making Trivial Changes ---------------------- The JIRA tickets are used to generate the changelog for the next release. For changes of a trivial nature to comments and documentation, it is not always necessary to create a new ticket in JIRA. In this case, it is appropriate to start the first line of a commit with '(doc)' instead of a ticket number. Submitting Changes ------------------ + Sign and submit the Apache [Contributor License Agreement][cla] if you haven't already. * Note that small patches & typical bug fixes do not require a CLA as clause 5 of the [Apache License](https://www.apache.org/licenses/LICENSE-2.0.html#contributions) covers them. + Push your changes to a topic branch in your fork of the repository. + Submit a _Pull Request_ to the corresponding repository in the `apache` organization. * Verify _Files Changed_ shows only your intended changes and does not include additional files like `target/*.class` + Update your JIRA ticket and include a link to the pull request in the ticket. If you prefer to not use GitHub, then you can instead use `git format-patch` (or `svn diff`) and attach the patch file to the JIRA issue. Additional Resources -------------------- + [Contributing patches](https://commons.apache.org/patches.html) + [Apache Commons Pool JIRA project page][jira] + [Contributor License Agreement][cla] + [General GitHub documentation](https://help.github.com/) + [GitHub pull request documentation](https://help.github.com/articles/creating-a-pull-request/) + [Apache Commons Twitter Account](https://twitter.com/ApacheCommons) + `#apache-commons` IRC channel on `irc.freenode.net` [cla]:https://www.apache.org/licenses/#clas [jira]:https://issues.apache.org/jira/browse/POOL commons-pool-rel-commons-pool-2.10.0/LICENSE.txt000066400000000000000000000266171405425132200212510ustar00rootroot00000000000000 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. APACHE COMMONS POOL DERIVATIVE WORKS: The LinkedBlockingDeque implementation is based on an implementation written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the public domain, as explained at http://creativecommons.org/licenses/publicdomain commons-pool-rel-commons-pool-2.10.0/NOTICE.txt000066400000000000000000000002561405425132200211370ustar00rootroot00000000000000Apache Commons Pool Copyright 2001-2021 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (https://www.apache.org/). commons-pool-rel-commons-pool-2.10.0/README.md000066400000000000000000000130401405425132200206670ustar00rootroot00000000000000 Apache Commons Pool =================== [![Travis-CI Status](https://travis-ci.org/apache/commons-pool.svg)](https://travis-ci.org/apache/commons-pool) [![GitHub Actions Status](https://github.com/apache/commons-pool/workflows/Java%20CI/badge.svg)](https://github.com/apache/commons-pool/actions) [![Coverage Status](https://coveralls.io/repos/apache/commons-pool/badge.svg)](https://coveralls.io/r/apache/commons-pool) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.commons/commons-pool2/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.apache.commons/commons-pool2/) [![Javadocs](https://javadoc.io/badge/org.apache.commons/commons-pool2/2.10.0.svg)](https://javadoc.io/doc/org.apache.commons/commons-pool2/2.10.0) The Apache Commons Object Pooling Library. Documentation ------------- More information can be found on the [Apache Commons Pool homepage](https://commons.apache.org/proper/commons-pool). The [Javadoc](https://commons.apache.org/proper/commons-pool/apidocs) can be browsed. Questions related to the usage of Apache Commons Pool should be posted to the [user mailing list][ml]. Where can I get the latest release? ----------------------------------- You can download source and binaries from our [download page](https://commons.apache.org/proper/commons-pool/download_pool.cgi). Alternatively you can pull it from the central Maven repositories: ```xml org.apache.commons commons-pool2 2.10.0 ``` Contributing ------------ We accept Pull Requests via GitHub. The [developer mailing list][ml] is the main channel of communication for contributors. There are some guidelines which will make applying PRs easier for us: + No tabs! Please use spaces for indentation. + Respect the code style. + Create minimal diffs - disable on save actions like reformat source code or organize imports. If you feel the source code should be reformatted create a separate PR for this change. + Provide JUnit tests for your changes and make sure your changes don't break any existing tests by running ```mvn clean test```. If you plan to contribute on a regular basis, please consider filing a [contributor license agreement](https://www.apache.org/licenses/#clas). You can learn more about contributing via GitHub in our [contribution guidelines](CONTRIBUTING.md). License ------- This code is under the [Apache Licence v2](https://www.apache.org/licenses/LICENSE-2.0). See the `NOTICE.txt` file for required notices and attributions. Donations --------- You like Apache Commons Pool? Then [donate back to the ASF](https://www.apache.org/foundation/contributing.html) to support the development. Additional Resources -------------------- + [Apache Commons Homepage](https://commons.apache.org/) + [Apache Issue Tracker (JIRA)](https://issues.apache.org/jira/browse/POOL) + [Apache Commons Twitter Account](https://twitter.com/ApacheCommons) + `#apache-commons` IRC channel on `irc.freenode.org` [ml]:https://commons.apache.org/mail-lists.html commons-pool-rel-commons-pool-2.10.0/README.txt000066400000000000000000000001501405425132200211040ustar00rootroot00000000000000See https://commons.apache.org/pool/ for additional and up-to-date information on Apache Commons Pool. commons-pool-rel-commons-pool-2.10.0/RELEASE-NOTES.txt000066400000000000000000000722531405425132200221320ustar00rootroot00000000000000 Apache Commons Pool 2.10.0 RELEASE NOTES The Apache Commons Pool team is pleased to announce the release of Apache Commons Pool 2.10.0. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. Version 2.10.x requires Java 8 or above. Version 2.9.x requires Java 8 or above. Version 2.8.x requires Java 8 or above. Version 2.7.x requires Java 8 or above. Version 2.6.x requires Java 7 or above. Version 2.5.x requires Java 7 or above. Version 2.0 requires 6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.3. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. This is a MMMM release (Java 8). Changes in version 2.10.0 include: New features: o Add and use java.time.Duration APIs timeouts instead of using ints for seconds. See the site and its API comparison report for a list of the new Duration-based APIs. Thanks to Gary Gregory. o Implement AbandonedConfig for GenericKeyedObjectPool #67. Thanks to JSurf, Gary Gregory, Phil Steitz. Fixed Bugs: o Simplify Assertions in tests #77. Thanks to Arturo Bernal. o Replace C-style array declaration with Java style #80. Thanks to Arturo Bernal. o Use Objects.equals(); Use Anonymous type; Use method reference instead Lambda; Replace Loop with Collection.removeIf(). #81. Thanks to Arturo Bernal. o Use diamond operator. #82. Thanks to Arturo Bernal. o Code clean ups. #83. Thanks to Arturo Bernal. Changes: o Bump spotbugs-maven-plugin from 4.0.4 to 4.2.1 #48, #53, #59, #62. Thanks to Dependabot. o Bump actions/setup-java from v1.4.2 to v2, #47. Thanks to Dependabot, Gary Gregory. o Bump junit from 4.13 to 4.13.1 #50. Thanks to Dependabot. o Bump biz.aQute.bndlib from 5.1.2 to 5.3.0, #51, #66. Thanks to Dependabot. o POOL-389: Migrate to JUnit 5 #57. Thanks to Arturo Bernal. o POOL-389: Minor Improvements #58, #60. Thanks to Arturo Bernal. o Bump actions/checkout from v2.3.3 to v2.3.4 #54. Thanks to Dependabot. o Bump maven-pmd-plugin from 3.13.0 to 3.14.0 #55. Thanks to Dependabot. o Update commons.japicmp.version 0.14.3 -> 0.15.3. Thanks to Gary Gregory. o Bump actions/cache from v2 to v2.1.6 #65, #75, #84. Thanks to Dependabot, Gary Gregory. o Bump maven-checkstyle-plugin from 3.1.1 to 3.1.2 #61. Thanks to Dependabot. o Bump asm-util from 9.0 to 9.1 #64. Thanks to Dependabot. o Bump spotbugs from 4.2.1 to 4.2.3 #68, #73, #74. Thanks to Dependabot. o Bump junit-bom from 5.7.1 to 5.8.0-M1 #76. Thanks to Dependabot, Gary Gregory. o Bump maven-bundle-plugin from 5.1.1 to 5.1.2 #70. Thanks to Dependabot. o Bump animal-sniffer-maven-plugin from 1.19 to 1.20. Thanks to Dependabot. For complete information on Apache Commons Pool, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Pool website: https://commons.apache.org/proper/commons-pool/ Download page: https://commons.apache.org/proper/commons-pool/download_pool.cgi ----------------------------------------------------------------------------------------------- Apache Commons Pool 2.9.0 RELEASE NOTES The Apache Commons Pool team is pleased to announce the release of Apache Commons Pool 2.9.0. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. Version 2.9.x requires Java 8 or above. Version 2.8.x requires Java 8 or above. Version 2.7.x requires Java 8 or above. Version 2.6.x requires Java 7 or above. Version 2.5.x requires Java 7 or above. Version 2.0 requires 6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.3. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. This is a minor release (Java 8). Changes in version 2.9.0 include: Changes: o POOL-387: Object factory destroy method should carry information on activation context. Thanks to Phil Steitz. o Update spotbugs from 4.0.6 to 4.1.3, #37, #41, #46. Thanks to Dependabot. o Update actions/checkout from v2.3.1 to v2.3.3 #56, #45. Thanks to Dependabot. o Update actions/setup-java from v1.4.0 to v1.4.2 #42. Thanks to Dependabot. o Update optional asm-util from 8.0.1 to 9.0 #44. Thanks to Dependabot. For complete information on Apache Commons Pool, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Pool website: https://commons.apache.org/proper/commons-pool/ Download page: https://commons.apache.org/proper/commons-pool/download_pool.cgi ----------------------------------------------------------------------------------------------- Apache Commons Pool 2.8.1 RELEASE NOTES The Apache Commons Pool team is pleased to announce the release of Apache Commons Pool 2.8.1. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. Version 2 requires JDK level 1.6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.3. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. This is a maintenance release (Java 8). Changes in version 2.8.1 include: New features: o POOL-385: Added Automatic-Module-Name to support JPMS #31. Thanks to scholzi100. Fixed Bugs: o POOL-386: Refactored EvictionTimer usage tracking to fix POOL-386 and handle abandoned pools. #32. Thanks to Phil Steitz, Mark Thomas. o [Javadoc] Add missing @throws comment in PoolUtils. #27. Thanks to Prodigysov, Gary Gregory. Changes: o POOL-384: Update optional library org.ow2.asm:asm-util from 7.2 to 8.0.1. Thanks to Gary Gregory. o Update site reports from org.apache.bcel:bcel 6.4.1 to 6.5.0. Thanks to Gary Gregory. o Update site reports from maven-pmd-plugin 3.12.0 to 3.13.0. Thanks to Gary Gregory. o Update build from biz.aQute.bnd:biz.aQute.bndlib 5.1.0 -> 5.1.2. Thanks to Gary Gregory. o Update actions/checkout from v1 to v2.3.1 #33. Thanks to Dependabot. o Update commons-parent from 50 to 51 #36. Thanks to Dependabot. o Update Checkstyle plugin from 3.0.0 to 3.1.1. Thanks to Gary Gregory. o Update JApiCmp from 0.14.1 to 0.14.3. Thanks to Gary Gregory. o Update animal-sniffer-maven-plugin from 1.16 to 1.19. Thanks to Gary Gregory. For complete information on Apache Commons Pool, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Pool website: https://commons.apache.org/proper/commons-pool/ Download page: https://commons.apache.org/proper/commons-pool/download_pool.cgi ----------------------------------------------------------------------------------------------- Apache Commons Pool 2.8.0 RELEASE NOTES 05 December 2019 The Apache Commons Pool team is pleased to announce the release of Apache Commons Pool 2.8.0. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. Version 2 requires JDK level 1.6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.3. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. This is a maintenance release (Java 8). Changes in version 2.8.0 include: New features: o POOL-378: Deprecate PoolUtils.prefill(ObjectPool, int) in favor of ObjectPool.addObjects(int). Thanks to Gary Gregory. o POOL-379: Deprecate PoolUtils.prefill(KeyedObjectPool, K, int) in favor of KeyedObjectPool.addObjects(K, int). Thanks to Gary Gregory. o POOL-380: Deprecate PoolUtils.prefill(KeyedObjectPool, Collection, int) in favor of KeyedObjectPool.addObjects(Collection, int). Thanks to Gary Gregory. Fixed Bugs: o POOL-374: org.apache.commons.pool2.impl.GenericKeyedObjectPool.returnObject(K, T) should throw IllegalStateException instead of NullPointerException when a key is not found in the pool map. Thanks to Gary Gregory, Phil Steitz. o POOL-376: Fixed regression from original fix for POOL-356 which could result in NPE when destroying objects. Thanks to Sazzadul Hoque, Phil Steitz. o POOL-326: Eliminated NPE / ISE exceptions due to keyed pools being prematurely removed. Thanks to Phil Steitz. o Close BufferedOutputStream in test before calling toString on underlying BufferedOutputStream #26. Thanks to emopers. o [Javadoc] Add missing @throws comment in SoftReferenceObjectPool. #28. Thanks to Prodigysov. Changes: o POOL-375: Update optional library cglib from 3.2.12 to 3.3.0. Thanks to Gary Gregory. o Update site build from Apache Commons BCEL 6.3.1 to 6.4.1. Thanks to Gary Gregory. o POOL-377: Update optional library org.ow2.asm:asm-util from 7.1 to 7.2. Thanks to Gary Gregory. For complete information on Apache Commons Pool, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Pool website: https://commons.apache.org/proper/commons-pool/ Download page: https://commons.apache.org/proper/commons-pool/download_pool.cgi ----------------------------------------------------------------------------------------------- Apache Commons Pool 2.7.0 RELEASE NOTES 23 July 2019 The Apache Commons Pool team is pleased to announce the release of Apache Commons Pool 2.7.0. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. Version 2 requires JDK level 1.6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.3. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. This is a feature release (Java 8). Changes in version 2.7.0 include: New features: o POOL-370: Add org.apache.commons.pool2.PooledObject#getBorrowedCount(). Thanks to Mark Thomas, Gary Gregory. o POOL-371: Add org.apache.commons.pool2.PooledObject#setRequireFullStackTrace(boolean). Thanks to Matt Sicker, Gary Gregory. Fixed Bugs: o POOL-361: Move validation for newly created objects into create(). Fixes #23. Thanks to Pablo, Phil Steitz, Bruno P. Kinoshita. Changes: o POOL-364: Update from Java 7 to Java 8. Thanks to Gary Gregory. o POOL-365: Update ASM from 7.0 to 7.1 Thanks to Gary Gregory. o POOL-366: Update optional library cglib from 3.2.10 to 3.2.12. Thanks to Gary Gregory. o POOL-367: Fix typo in package private method name stopEvitor() -> stopEvictor() #22. Thanks to Per Lundberg. For complete information on Apache Commons Pool, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Pool website: https://commons.apache.org/proper/commons-pool/ Download page: https://commons.apache.org/proper/commons-pool/download_pool.cgi ----------------------------------------------------------------------------------------------- Apache Commons Pool 2.6.2 RELEASE NOTES The Apache Commons Pool team is pleased to announce the release of Apache Commons Pool 2.6.2. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. Version 2 requires JDK level 1.6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.3. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. This is a maintenance release. Changes in version 2.6.2 include: Fixed Bugs: o POOL-362: Always null out org.apache.commons.pool2.impl.BaseGenericObjectPool.evictionIterator to match org.apache.commons.pool2.impl.BaseGenericObjectPool.evictor. o POOL-363: Evictor Thread prevents Spring Context shutdown in standalone app. Thanks to Josh Landin. o POOL-348: The commons-pool-evictor-thread should run as a Deamon. Thanks to Josh Landin. For complete information on Apache Commons Pool, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Pool website: https://commons.apache.org/proper/commons-pool/ Download page: https://commons.apache.org/proper/commons-pool/download_pool.cgi ----------------------------------------------------------------------------------------------- Apache Commons Pool 2.6.1 RELEASE NOTES The Apache Commons Pool team is pleased to announce the release of Apache Commons Pool 2.6.1. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. Version 2 requires JDK level 1.6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.3. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. This is a maintenance release. Changes in version 2.6.1 include: Fixed Bugs: o POOL-340: Correct validateObject with concurrent borrowObject Thanks to Pavel Kolesov. o POOL-356: Fix deadlock on massive concurrent requests o POOL-347: Method borrowObject waits for maxWaitMillis over in pool full. Thanks to Shunsuke Nakamura. o POOL-359: NullPointerException closing multiple GenericObjectPools. Thanks to Michael Wintermeyer, Gary Gregory. o POOL-326: Threading issue, NullPointerException and IllegalStateException in GenericKeyedObjectPool. Thanks to Chris Allison, Phil Steitz. o POOL-352: CallStackUtils mishandles security manager check (partial fix.) Thanks to Volker Kleinschmidt, Gary Gregory. Changes: o POOL-345: Update optional library cglib from 3.2.6 to 3.2.9. o POOL-346: Move common configuration setter to BaseGenericObjectPool #9. Thanks to Michael Chen. o POOL-349: Update optional library asm-util from 6.2 to 7.0. o POOL-360: Update optional library cglib from 3.2.9 to 3.2.10. For complete information on Apache Commons Pool, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Pool website: https://commons.apache.org/proper/commons-pool/ Download page: https://commons.apache.org/proper/commons-pool/download_pool.cgi ----------------------------------------------------------------------------------------------- Apache Commons Pool 2.6.0 RELEASE NOTES The Apache Commons Pool team is pleased to announce the release of Apache Commons Pool 2.6.0. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. - Version 2.6.0 requires Java 7 or above. - Version 2.5.0 requires Java 7 or above. - Version 2.0 requires 6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.3. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. This is a maintenance release. Changes in version 2.6.0 include: Fixed Bugs: o POOL-337: Ensure cancelled eviction tasks are removed from scheduler. Thanks to Reinald Verheij. o POOL-338: GenericObjectPool constructor may throw an exception under OSGi. Thanks to Michael C, Gary Gregory. o POOL-324: org.apache.commons.pool2.impl.GenericObjectPool.getFactoryType() throws java.lang.ClassCastException. Thanks to Jay Xu, Gary Gregory. o POOL-344: Delete repeated call startEvictor. Thanks to Yulin Wang. Changes: o POOL-336: GenericObjectPool's borrowObject lock if create() fails with Error. Thanks to Wolfgang Glas. o POOL-339: Update optional library cglib from 3.2.5 to 3.2.6. o POOL-341: Update optional library asm-util from 6.0 to 6.1.1. o POOL-342: Update optional library asm-util from 6.1.1 to 6.2. Note that Clirr reports one warning: "Value of field DEFAULT_EVICTION_POLICY_CLASS_NAME is no longer a compile-time constant." The value is initialized as "public static final String DEFAULT_EVICTION_POLICY_CLASS_NAME = DefaultEvictionPolicy.class.getName();" The value should not change from one run to the next. For complete information on Apache Commons Pool, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Pool website: https://commons.apache.org/proper/commons-pool/ Download page: https://commons.apache.org/proper/commons-pool/download_pool.cgi ----------------------------------------------------------------------------------------------- Apache Commons Pool 2.5.0 RELEASE NOTES The Apache Commons Pool team is pleased to announce the release of Apache Commons Pool 2.5.0. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. Version 2 requires JDK level 1.6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.3. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. This is a patch release, including bug fixes only. Changes in version 2.5.0 include: New features: o POOL-332: ObjectPool and KeyedObject pool should extend Closeable. o POOL-335: Make abandoned logging stack trace requirements configurable. This also reverts the default behavior introduced by POOL-320. Changes: o POOL-331: Update from Java 6 to 7. o POOL-333: Update optional dependency asm-util from 5.2 to 6.0. o POOL-334: org.apache.commons.pool2.impl.ThrowableCallStack.Snapshot is missing serialVersionUID. For complete information on Apache Commons Pool, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Pool website: https://commons.apache.org/proper/commons-pool/ ----------------------------------------------------------------------------------------------- Apache Commons Pool 2.4.3 RELEASE NOTES The Apache Commons Pool team is pleased to announce the release of Apache Commons Pool 2.4.3. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. Version 2 requires JDK level 1.6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.3. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. Changes in version 2.4.3 include: New features: o POOL-320: Use more efficient stack walking mechanisms for usage tracking when possible. Fixed Bugs: o POOL-328: Documentation with repeated words (sources, tests, and examples). Thanks to Lorenzo Solano Martinez. o POOL-317: Correction of default value of softMinEvictableIdleTimeMillis in BaseObjectPoolConfig. Thanks to KeiichiFujino. o POOL-309: Fix misspellings from "destory" to "destroy". Thanks to jolestar, Roopam Patekar. o POOL-306: Ensure BaseGenericObjectPool.IdentityWrapper#equals() follows the expected contract for equals(). Thanks to Adrian Crum. o POOL-303: Ensure that threads do not block indefinitely if more than maxTotal threads try to borrow an object at the same time and the factory fails to create any objects. o POOL-310: Ensure that threads using GKOP do not block indefinitely if more than maxTotal threads try to borrow objects with different keys at the same time and the factory destroys objects on return. Thanks to Ivan Iliev. o Ensure that any class name used for evictionPolicyClassName represents a class that implements EvictionPolicy. o POOL-315: Add a configurable delay (default 10 seconds) to wait when shutting down an Evictor to allow the associated thread time to complete and current evictions and to terminate. Thanks to KeiichiFujino. o Ensure that a call to GKOP preparePool() takes account of other threads that might create objects concurrently, particularly the Evictor. Changes: o POOL-280: Small refactoring of borrowObject() to reduce code duplication. Thanks to Jacopo Cappellato. o POOL-307: Replace inefficient use of keySet with entrySet in GKOP. Thanks to Anthony Whitford. o POOL-322: Update optional cglib library from 3.1 to 3.2.5. o POOL-323: Update optional OW2 ASM from 5.0.4 to 5.2. For complete information on Apache Commons Pool, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Pool website: https://commons.apache.org/proper/commons-pool/ ----------------------------------------------------------------------------------------------- Apache Commons Pool 2.4.2 RELEASE NOTES The Apache Commons Pool team is pleased to announce the release of Apache Commons Pool 2.4.2. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. Version 2 requires JDK level 1.6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.2. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. This is a patch release, including bug fixes only. Changes in version 2.4.2 include: Fixed Bugs: o POOL-298: Changed default jmxNameBase in BaseObjectPoolConfig to the correct (null) default. o POOL-300: Added PrintWriter flush to DefaultPooledObject's printStackTrace method. For complete information on Apache Commons Pool, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Pool website: https://commons.apache.org/proper/commons-pool/ commons-pool-rel-commons-pool-2.10.0/SECURITY.md000066400000000000000000000016041405425132200212040ustar00rootroot00000000000000 The Apache Commons security page is [https://commons.apache.org/security.html](https://commons.apache.org/security.html). commons-pool-rel-commons-pool-2.10.0/checkstyle.xml000066400000000000000000000047551405425132200223050ustar00rootroot00000000000000 commons-pool-rel-commons-pool-2.10.0/doc/000077500000000000000000000000001405425132200201575ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/doc/ReaderUtil.java000066400000000000000000000043231405425132200230640ustar00rootroot00000000000000/* * 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. */ /* * This file is one of the source files for the examples contained in * /src/site/xdoc/examples.xml * It is not intended to be included in a source release. */ import java.io.IOException; import java.io.Reader; import org.apache.commons.pool2.ObjectPool; /** * Maintains a pool of StringBuffers used to dump contents of Readers. */ public class ReaderUtil { private ObjectPool pool; public ReaderUtil(ObjectPool pool) { this.pool = pool; } /** * Dumps the contents of the {@link Reader} to a String, closing the {@link Reader} when done. */ public String readToString(Reader in) throws IOException { StringBuffer buf = null; try { buf = pool.borrowObject(); for (int c = in.read(); c != -1; c = in.read()) { buf.append((char) c); } return buf.toString(); } catch (IOException e) { throw e; } catch (Exception e) { throw new RuntimeException("Unable to borrow buffer from pool" + e.toString()); } finally { try { in.close(); } catch (Exception e) { // ignored } try { if (null != buf) { pool.returnObject(buf); } } catch (Exception e) { // ignored } } } } commons-pool-rel-commons-pool-2.10.0/doc/ReaderUtilClient.java000066400000000000000000000031701405425132200242220ustar00rootroot00000000000000/* * 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. */ /* * This file is one of the source files for the examples contained in * /src/site/xdoc/examples.xml * It is not intended to be included in a source release. */ import java.io.IOException; import java.io.Reader; import java.io.StringReader; import org.apache.commons.pool2.impl.GenericObjectPool; /** * Instantiates and uses a ReaderUtil. The GenericObjectPool supplied to the constructor will have * default configuration properties. */ public class ReaderUtilClient { public static void main(String[] args) { ReaderUtil readerUtil = new ReaderUtil(new GenericObjectPool(new StringBufferFactory())); Reader reader = new StringReader("foo"); try { System.out.println(readerUtil.readToString(reader)); } catch (IOException e) { e.printStackTrace(); } } } commons-pool-rel-commons-pool-2.10.0/doc/StringBufferFactory.java000066400000000000000000000036001405425132200247510ustar00rootroot00000000000000/* * 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. */ /* * This file is one of the source files for the examples contained in * /src/site/xdoc/examples.xml * It is not intended to be included in a source release. */ import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.impl.DefaultPooledObject; /** * Example PooledObjectFactory for pooled StringBuffers. */ public class StringBufferFactory extends BasePooledObjectFactory { @Override public StringBuffer create() { return new StringBuffer(); } /** * Use the default PooledObject implementation. */ @Override public PooledObject wrap(StringBuffer buffer) { return new DefaultPooledObject(buffer); } /** * When an object is returned to the pool, clear the buffer. */ @Override public void passivateObject(PooledObject pooledObject) { pooledObject.getObject().setLength(0); } // for all other methods, the no-op implementation // in BasePooledObjectFactory will suffice } commons-pool-rel-commons-pool-2.10.0/findbugs-exclude-filter.xml000066400000000000000000000176701405425132200246620ustar00rootroot00000000000000 commons-pool-rel-commons-pool-2.10.0/pom.xml000066400000000000000000000423431405425132200207350ustar00rootroot00000000000000 4.0.0 org.apache.commons commons-parent 52 commons-pool2 2.10.0 Apache Commons Pool 2001 The Apache Commons Object Pooling Library. https://commons.apache.org/proper/commons-pool/ jira https://issues.apache.org/jira/browse/POOL scm:git:https://gitbox.apache.org/repos/asf/commons-pool.git scm:git:https://gitbox.apache.org/repos/asf/commons-pool.git https://gitbox.apache.org/repos/asf?p=commons-pool.git Morgan Delagrange morgand Geir Magnusson geirm Craig McClanahan craigmcc Rodney Waldhoff rwaldhoff David Weinrich dweinr1 Dirk Verbeeck dirkv Robert Burrell Donkin rdonkin The Apache Software Foundation Sandy McArthur sandymac The Apache Software Foundation Simone Tripodi simonetripodi The Apache Software Foundation Gary Gregory ggregory The Apache Software Foundation Matt Sicker mattsicker The Apache Software Foundation Todd Carmichael toddc@concur.com cglib cglib 3.3.0 true org.ow2.asm asm-util 9.1 true org.junit.jupiter junit-jupiter test apache.website Apache Commons Site scm:svn:https://svn.apache.org/repos/infra/websites/production/commons/content/proper/commons-pool/ UTF-8 UTF-8 1.8 1.8 pool org.apache.commons.pool2 RC1 2.10.0 (Java 8) 2.6.2 (Java 7) 2.4.3 (Java 6) sha256 1.6 (Java 5) sha256 -bin commons-pool-${commons.release.4.version} POOL 12310488 site-content net.sf.cglib.proxy;resolution:=optional,* 1.20 3.1.2 5.1.2 4.2.3 4.2.3 2.9.0 true Gary Gregory 86fdc7e2a11262cb 0.15.3 true false 5.8.0-M1 false clean verify apache-rat:check checkstyle:check japicmp:cmp javadoc:javadoc spotbugs:check org.apache.felix maven-bundle-plugin ${commons.felix.version} biz.aQute.bnd biz.aQute.bndlib 5.3.0 org.apache.maven.plugins maven-release-plugin false true org.apache.maven.plugins maven-project-info-reports-plugin ${commons.project-info.version} org.apache.bcel bcel 6.5.0 org.apache.maven.plugins maven-checkstyle-plugin ${commons.checkstyle-plugin.version} ${basedir}/checkstyle.xml false ${basedir}/license-header.txt com.github.spotbugs spotbugs-maven-plugin ${spotbugs.plugin.version} com.github.spotbugs spotbugs ${spotbugs.impl.version} ${basedir}/findbugs-exclude-filter.xml org.apache.rat apache-rat-plugin src/test/resources/test1 src/test/resources/test2 .checkstyle .fbprefs .pmd org.apache.maven.plugins maven-surefire-plugin 3.0.0-M5 1800 **/Test*.java **/Test*$*.java **/TestSoftRefOutOfMemory.java maven-assembly-plugin src/assembly/bin.xml src/assembly/src-tar-gz.xml src/assembly/src-zip.xml gnu org.apache.maven.plugins maven-scm-publish-plugin api-* com.github.siom79.japicmp japicmp-maven-plugin METHOD_NEW_DEFAULT true true PATCH org.apache.maven.plugins maven-jar-plugin ${commons.module.name} org.apache.maven.plugins maven-changes-plugin ${commons.changes.version} ${basedir}/src/changes/changes.xml src/changes changes-report org.apache.maven.plugins maven-checkstyle-plugin com.github.spotbugs spotbugs-maven-plugin org.apache.rat apache-rat-plugin src/test/resources/test1 src/test/resources/test2 .checkstyle .fbprefs .pmd com.github.siom79.japicmp japicmp-maven-plugin METHOD_NEW_DEFAULT true true PATCH maven-pmd-plugin 3.14.0 ${maven.compiler.target} pmd cpd org.junit junit-bom ${junit.version} pom import java9 9 true java11 [11,) true java16 [16,) org.apache.maven.plugins maven-surefire-plugin --illegal-access=permit --add-opens java.base/java.lang=ALL-UNNAMED commons-pool-rel-commons-pool-2.10.0/pool-RC.sh000077500000000000000000000045061405425132200212310ustar00rootroot00000000000000#!/bin/sh # # 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. # ----------------------------------------------------------------------------- # Generates a pool RC and publishes (a superset of) maven artifacts to Nexus. # Should be run from top-level directory of a fresh checkout of the RC tag. # # Preconditions: # 0) pool-pre-RC has been run to update the download page and release notes # and these have been checked in and included in the RC tag. # 1) Release artifacts from previous runs have been svn deleted from local # svn pub/sub dev checkout. # 2) Nexus repo from previous RC has been dropped. # # $Revision$ $Date$ # ----------------------------------------------------------------------------- # Set script variables version=2.4.3 repo_path=~/.m2/repository/org/apache/commons/commons-pool2/${version} release_path=~/pool-rc #checkout of https://dist.apache.org/repos/dist/dev/commons/pool # # Delete any locally installed artifacts from previous runs rm -rf ${repo_path} echo "Cleaned maven repo." # # Generate site and release artifacts, deploy locally and upload to Nexus mvn clean site mvn deploy -Prelease # # Copy the zips/tarballs and release notes to the local svn pub path cp ${repo_path}/*bin.zip* ${release_path}/binaries cp ${repo_path}/*bin.tar.gz* ${release_path}/binaries cp ${repo_path}/*src.zip* ${release_path}/source cp ${repo_path}/*src.tar.gz* ${release_path}/source cp RELEASE-NOTES.txt ${release_path} echo "Release candidate complete." echo "svn add the generated artifacts and commit after inspection." echo "log in to repository.apache.org, manually (sic) drop the cruft and close the repo." commons-pool-rel-commons-pool-2.10.0/pool-pre-RC.sh000077500000000000000000000026441405425132200220160ustar00rootroot00000000000000#!/bin/sh # # 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. # ----------------------------------------------------------------------------- # # Shell script to update download page and release notes prior # to preparing a commons pool release candidate. # # Note: RELEASE-NOTES.txt may need a little reformatting prior # to checkin. Both RELEASE-NOTES.txt and the generated download # page need to be checked in after review. # # $Revision$ $Date$ # ---------------------------------------------------------------------------- version=2.4.3 mvn changes:announcement-generate -Prelease-notes -Dchanges.version=${version} mvn commons:download-page -Dcommons.componentid=pool -Dcommons.release.version=${version} commons-pool-rel-commons-pool-2.10.0/pool-release.sh000077500000000000000000000050211405425132200223360ustar00rootroot00000000000000#!/bin/sh # # 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. # ----------------------------------------------------------------------------- # Performs the local svn steps necessary to publish a pool release. # # Preconditions: # 0) Successful release VOTE has completed, based on artifacts in rc_path # (checkout of https://dist.apache.org/repos/dist/dev/commons/pool) # 1) release_path points to a local checkout of # https://dist.apache.org/repos/dist/release/commons/pool # 2) RELEASE-NOTES.txt for the new release is in top level of rc_path # # NOTE: This script does not do any of the following: # 0) Commit the local changes to actually publish the artifacts # 1) Cleanup old versions in dist # # $Revision$ $Date$ # ----------------------------------------------------------------------------- # Set script variables version=2.4.3 # version being released last_version=2.4.2 # previous version, will be replaced in README.html rc_path=~/pool-rc # checkout of https://dist.apache.org/repos/dist/dev/commons/pool release_path=~/pool-release #https://dist.apache.org/repos/dist/release/commons/pool # # Move release notes cp $rc_path/RELEASE-NOTES.txt $release_path svn rm $rc_path/RELEASE-NOTES.txt # # Update README.html sed -i "" "s/$last_version/$version/g" $release_path/README.html # OSX ^^ required suffix # cp $release_path/README.html $release_path/source cp $release_path/README.html $release_path/binaries # ^^^^^^^^^^ Maybe we can toss these? ^^^^^^^ # # Move release artifacts svn mv $rc_path/source/* $release_path/source svn mv $rc_path/binaries/* $release_path/binaries # echo "Local svn changes complete." echo "Inspect the files in $release_path and commit to publish the release." echo "Also remember to commit $rc_path to drop RC artifacts and svn rm" echo "obsolete artifacts from $release_path." commons-pool-rel-commons-pool-2.10.0/src/000077500000000000000000000000001405425132200202015ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/assembly/000077500000000000000000000000001405425132200220205ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/assembly/bin.xml000066400000000000000000000030611405425132200233120ustar00rootroot00000000000000 bin tar.gz zip false LICENSE.txt NOTICE.txt RELEASE-NOTES.txt target *.jar target/site/apidocs apidocs commons-pool-rel-commons-pool-2.10.0/src/assembly/src-tar-gz.xml000066400000000000000000000051031405425132200245320ustar00rootroot00000000000000 src tar.gz ${project.artifactId}-${project.version}-src build.xml checkstyle.xml LICENSE.txt license-header.txt NOTICE.txt pom.xml README.txt RELEASE-NOTES.txt findbugs-exclude-filter.xml build.properties.sample lf doc lf src/site/resources src/site/xdoc lf src/site site.xml lf src/changes lf src/main lf src/test/java lf src/test/resources commons-pool-rel-commons-pool-2.10.0/src/assembly/src-zip.xml000066400000000000000000000051151405425132200241330ustar00rootroot00000000000000 src zip ${project.artifactId}-${project.version}-src build.xml checkstyle.xml LICENSE.txt license-header.txt NOTICE.txt pom.xml README.txt RELEASE-NOTES.txt findbugs-exclude-filter.xml build.properties.sample crlf doc crlf src/site/resources src/site/xdoc crlf src/site site.xml crlf src/changes crlf src/main crlf src/test/java crlf src/test/resources commons-pool-rel-commons-pool-2.10.0/src/changes/000077500000000000000000000000001405425132200216115ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/changes/changes.xml000066400000000000000000001443761405425132200237620ustar00rootroot00000000000000 Apache Commons Pool Release Notes Add and use java.time.Duration APIs timeouts instead of using ints for seconds. See the site and its API comparison report for a list of the new Duration-based APIs. Implement AbandonedConfig for GenericKeyedObjectPool #67. Simplify Assertions in tests #77. Replace C-style array declaration with Java style #80. Use Objects.equals(); Use Anonymous type; Use method reference instead Lambda; Replace Loop with Collection.removeIf(). #81. Use diamond operator. #82. Code clean ups. #83. Bump spotbugs-maven-plugin from 4.0.4 to 4.2.1 #48, #53, #59, #62. Bump actions/setup-java from v1.4.2 to v2, #47. Bump junit from 4.13 to 4.13.1 #50. Bump biz.aQute.bndlib from 5.1.2 to 5.3.0, #51, #66. Migrate to JUnit 5 #57. Minor Improvements #58, #60. Bump actions/checkout from v2.3.3 to v2.3.4 #54. Bump maven-pmd-plugin from 3.13.0 to 3.14.0 #55. Update commons.japicmp.version 0.14.3 -> 0.15.3. Bump actions/cache from v2 to v2.1.6 #65, #75, #84. Bump maven-checkstyle-plugin from 3.1.1 to 3.1.2 #61. Bump asm-util from 9.0 to 9.1 #64. Bump spotbugs from 4.2.1 to 4.2.3 #68, #73, #74. Bump junit-bom from 5.7.1 to 5.8.0-M1 #76. Bump maven-bundle-plugin from 5.1.1 to 5.1.2 #70. Bump animal-sniffer-maven-plugin from 1.19 to 1.20. Object factory destroy method should carry information on activation context. Update spotbugs from 4.0.6 to 4.1.3, #37, #41, #46. Update actions/checkout from v2.3.1 to v2.3.3 #56, #45. Update actions/setup-java from v1.4.0 to v1.4.2 #42. Update optional asm-util from 8.0.1 to 9.0 #44. Refactored EvictionTimer usage tracking to fix POOL-386 and handle abandoned pools. #32. [Javadoc] Add missing @throws comment in PoolUtils. #27. Update optional library org.ow2.asm:asm-util from 7.2 to 8.0.1. Added Automatic-Module-Name to support JPMS #31. Update site reports from org.apache.bcel:bcel 6.4.1 to 6.5.0. Update site reports from maven-pmd-plugin 3.12.0 to 3.13.0. Update build from biz.aQute.bnd:biz.aQute.bndlib 5.1.0 -> 5.1.2. Update actions/checkout from v1 to v2.3.1 #33. Update commons-parent from 50 to 51 #36. Update Checkstyle plugin from 3.0.0 to 3.1.1. Update JApiCmp from 0.14.1 to 0.14.3. Update animal-sniffer-maven-plugin from 1.16 to 1.19. org.apache.commons.pool2.impl.GenericKeyedObjectPool.returnObject(K, T) should throw IllegalStateException instead of NullPointerException when a key is not found in the pool map. Update optional library cglib from 3.2.12 to 3.3.0. Fixed regression from original fix for POOL-356 which could result in NPE when destroying objects. Eliminated NPE / ISE exceptions due to keyed pools being prematurely removed. Update site build from Apache Commons BCEL 6.3.1 to 6.4.1. Update optional library org.ow2.asm:asm-util from 7.1 to 7.2. Deprecate PoolUtils.prefill(ObjectPool, int) in favor of ObjectPool.addObjects(int). Deprecate PoolUtils.prefill(KeyedObjectPool, K, int) in favor of KeyedObjectPool.addObjects(K, int). Deprecate PoolUtils.prefill(KeyedObjectPool, Collection, int) in favor of KeyedObjectPool.addObjects(Collection, int). Close BufferedOutputStream in test before calling toString on underlying BufferedOutputStream #26. [Javadoc] Add missing @throws comment in SoftReferenceObjectPool. #28. Update from Java 7 to Java 8. Update ASM from 7.0 to 7.1 Update optional library cglib from 3.2.10 to 3.2.12. Fix typo in package private method name stopEvitor() -> stopEvictor() #22. Move validation for newly created objects into create(). Fixes #23. Add org.apache.commons.pool2.PooledObject#getBorrowedCount(). Add org.apache.commons.pool2.PooledObject#setRequireFullStackTrace(boolean). CallStackUtils mishandles security manager check part 1. Always null out org.apache.commons.pool2.impl.BaseGenericObjectPool.evictionIterator to match org.apache.commons.pool2.impl.BaseGenericObjectPool.evictor. Evictor Thread prevents Spring Context shutdown in standalone app. The commons-pool-evictor-thread should run as a Deamon. Correct validateObject with concurrent borrowObject Fix deadlock on massive concurrent requests Update optional library cglib from 3.2.6 to 3.2.9. Move common configuration setter to BaseGenericObjectPool #9. Method borrowObject waits for maxWaitMillis over in pool full. Update optional library asm-util from 6.2 to 7.0. NullPointerException closing multiple GenericObjectPools. Update optional library cglib from 3.2.9 to 3.2.10. Threading issue, NullPointerException and IllegalStateException in GenericKeyedObjectPool. CallStackUtils mishandles security manager check (partial fix.) GenericObjectPool's borrowObject lock if create() fails with Error. Update optional library cglib from 3.2.5 to 3.2.6. Ensure cancelled eviction tasks are removed from scheduler. Update optional library asm-util from 6.0 to 6.1.1. Update optional library asm-util from 6.1.1 to 6.2. GenericObjectPool constructor may throw an exception under OSGi. org.apache.commons.pool2.impl.GenericObjectPool.getFactoryType() throws java.lang.ClassCastException. Delete repeated call startEvictor. Update from Java 6 to 7. Drop Ant build. ObjectPool and KeyedObject pool should extend Closeable. Update optional dependency asm-util from 5.2 to 6.0. org.apache.commons.pool2.impl.ThrowableCallStack.Snapshot is missing serialVersionUID. Make abandoned logging stack trace requirements configurable. This also reverts the default behavior introduced by POOL-320. Make abandoned logging stack trace requirements configurable. This also reverts the default behavior introduced by POOL-320. Documentation with repeated words (sources, tests, and examples). Correction of default value of softMinEvictableIdleTimeMillis in BaseObjectPoolConfig. Fix misspellings from "destory" to "destroy". Ensure BaseGenericObjectPool.IdentityWrapper#equals() follows the expected contract for equals(). Ensure that threads do not block indefinitely if more than maxTotal threads try to borrow an object at the same time and the factory fails to create any objects. Small refactoring of borrowObject() to reduce code duplication. Replace inefficient use of keySet with entrySet in GKOP. Ensure that threads using GKOP do not block indefinitely if more than maxTotal threads try to borrow objects with different keys at the same time and the factory destroys objects on return. Ensure that any class name used for evictionPolicyClassName represents a class that implements EvictionPolicy. Add a configurable delay (default 10 seconds) to wait when shutting down an Evictor to allow the associated thread time to complete any current evictions and to terminate. Ensure that a call to GKOP preparePool() takes account of other threads that might create objects concurrently, particularly the Evictor. Use more efficient stack walking mechanisms for usage tracking when possible. Update optional cglib library from 3.1 to 3.2.5. Update optional OW2 ASM from 5.0.4 to 5.2. Changed default jmxNameBase in BaseObjectPoolConfig to the correct (null) default. Added PrintWriter flush to DefaultPooledObject's printStackTrace method. Reverted cobertura plugin update that caused binary jar corruption. Fixed capacity leak when an object is offered from a GenericKeyedObjectPool while it is being validated by the evictor. Eliminated the requirement that objects managed by GenericObjectPool or GenericKeyedObjectPool be discernible by equals. Prior to this fix, equal but distinct object instances could not be stored in the same pool. Eliminated the requirement that object equality and hashcodes do not change while objects are under management by GenericObjectPool or GenericKeyedObjectPool. Fixed class loading for custom EvictionPolicy implementations that may not be present in the class loader hierarchy of the Pool classes by falling back to the class loader of the current class. Ensured that when an instance that has already been returned to a pool is returned again, the expected IllegalStateException is generated before the returning object is re-validated or re-passivated. Added preparePool method to GenericObjectPool. Update asm-util from 5.0.3 to 5.0.4. Exposed getEvictionPolicy as protected in BaseGenericObjectPool. Eliminated possibility that DefaultPoolObject#getIdleTimeMillis() could return a negative value. Use by pool implementations would not hit this bug. Made wrapped BaseProxyHandler.pooledObject volatile. Replace synchronisation with lock-free maxBorrowWaitTimeMillis to increase scalability. Ensure that objects are not validated on borrow when testOnBorrow is set to false, testOnCreate is set to true and the pool is exhausted at the point borrowObject() is called. Fixed error in GenericKeyedObjectPool constructor causing minEvictableIdleTimeMillis to be used in place of timeBetweenEvictionRunsMillis in eviction timer setup when a GenericKeyedObjectPoolConfig instance is supplied to the constructor. Fix a threading issue that meant that concurrent calls to close() and returnObject() could result in some returned objects not being destroyed. Made fairness configurable for GenericObjectPool, GenericKeyedObjectPool. Correctly mark cglib as an optional dependency and ensure that the OSGi manifest information reflects that. Improve performance of statistics collection for pools that extend BaseGenericObjectPool. Made client wait time statistics accurate when pools are configured to block indefinitely. Also modified computation to include latency clients experience due to waiting on factory methods. Update cglib to 3.1 from 3.0. Update asm-util to 5.0.3 from 4.0. Prevent potential memory leaks when the Pool is dereferenced without being closed. Prevent potential memory leaks with using an Evictor in a container environment. Protect against a user provided eviction policy throwing an exception and stopping the Evictor thread. Use the thread context class loader to load custom eviction policies. This allows application provided eviction policies to be used in a container environment when the pooling implementation is provided by the container. Fix a potential infinite loop in the underlying Deque implementation. Ensure that if an attempt is made to return an object multiple times that the current active and idle object counts are not corrupted. Fix Javadoc issues when building docs with Java 8. Fix the remaining Javadoc warnings. Add a new validation configuration option testOnCreate that tests an object immediately after it is created. Added missing create counter decrement in GenericKeyedObjectPool create method on factory exception path. Prior to this fix, exceptions thrown by factory makeObject calls could leak per key capacity. Ensured that blocked threads waiting on a depleted pool get served when objects are destroyed due to validation or passivation failures in returnObject or when a checked out instance is invalidated. Expand the coverage of the unit tests. Provide more control over the names under which Pools are registered in JMX so components using the pools can register the pools they use under a related name. Include the number of times an object has been borrowed from the Pool when the DefaultPooledObject wrapper is used and expose this property via JMX. Remove a duplicate null check and fix some typos in PoolUtils. Make the toString() method of ErodingKeyedObjectPool consistent with the other pools. Further expansion of the coverage of the unit tests. Add support for proxy wrappers to ObjectPool and KeyedObjectPool. The primary advantage of these wrappers is that use of pooled objects is prevented after they have been returned to the pool. Added abandoned object removal (moved from DBCP) to GenericObjectPool. PooledObject.state does not need to be volatile Suppress a FindBugs warning Use an IODH for PoolUtils.MIN_IDLE_TIMER GenericKeyedObjectPool.ensureMinIdle(K) does not need to check getMinIdlePerKey(). GenericKeyedObjectPool - multiple mutable fields not published safely. GenericObjectPool.evictionPolicy not thread-safe GenericObjectPool allows maxIdle < minIdle GenericKeyedObjectPool.clear() has unnecessary null check of objectDequeue Classes Generic[Keyed]ObjectPoolConfig are generic - but why? GOP/GKOP don't consistently use getters to access fields GOP/GKOP evict() method is not synchronized and is not thread-safe PooledObject: risky init of lastBorrowTime & lastReturnTime PooledObject.getActiveTimeMillis() does not synch. access to lastReturnTime and lastBorrowTime Replace synchronized blocks in PoolUtils with Read/Write locks. Support Java 1.5 Generics. Developer documentation and examples have to be updated once the 2.0 repackaging has been done Switch GOP to use a pooling mechanism based on java.util.concurrent and a LinkedBlockingQueue implementation from Apache Harmony (originally by Doug Lea and the JSR-166 expert group). Make deprecated protected attributes private, requiring that access is via the appropriate getters. Code clean-up. Add missing @Override annotations, remove unused code, remove deprecated code and unnecessary code. Introduce an Enum (WhenExhaustedAction) to control pool behavior when no more objects are available to allocate. Remove WhenExhuastedAction.GROW since it is equivalent to WhenExhuastedAction.FAIL with a maxActive value of Integer.MAX_VALUE. Remove confusing method PoolUtils.ErodingKeyedObjectPool.numIdle(K key). Guard against multiple returns of the same object to the pool and ensure that only objects borrowed from the pool are returned to it. Ensure GKOP.preparePool() throws an exception if no factory has been defined. Add the ability to specify a per call wait time when borrowing an object. Provide a name for the eviction timer thread. Remove setFactory() method from GOP. Reduce duplication in configuration code. Re-factor common code into common base classes. Expose GOP and GKOP attributes via JMX. Add additional attributes (also accessible via JMX) for monitoring. Change meaning of zero for maxWait to a maximum wait of zero milliseconds rather than the unexpected infinite wait. Allow custom eviction policies to be defined. Add support for proxy wrappers for ObjectPool and KeyedObjectPool. The primary advantage of using these wrappers is that use of pooled objects is prevented after they have been returned to the pool. Support Java 1.5 Generics in version 1.x. Awaken threads waiting on borrowObject when a pool has been closed and have them throw IllegalStateException. Prior to the fix for this issue, threads waiting in borrowObject when close was invoked on GOP or GKOP would block indefinitely. Corrected total internal processing counter update in destroy. Prior to the fix for this issue, clear(key) was leaking capacity associated with elements in the pool being cleared. Correctly handle an InterruptedException when waiting for an object from the pool. Only stop tracking objects for a key when there are no idle objects, no active objects and no objects being processed. Make BaseObjectPool.isClosed() public. Correct bug that could lead to inappropriate pool starvation when evict() and borrowObject() are called concurrently. Fix performance issues when object destruction has latency. In preparation for pool 2.0, deprecated direct access to protected fields and setFactory methods. In pool 2.0, pool object factories will be immutable. Made GenericKeyedObjectPool._minIdle volatile. Made the default context class loader for the eviction thread the same loader that loads the library to prevent memory leaks in multiple class loader environments. GenericKeyedObjectPool.destroy could use entrySet() rather than keySet() followed by get() GenericObjectPool and GenericKeyedObjectPool setFactory methods destroy idle instances in the pool by contract. Prior to the fix for this issue, newly set factories were being used to destroy idle instances, rather than the factories used to create them. The setFactory methods have also been deprecated, to be removed in version 2.0. ObjectPool classes can ignore Throwable. Added consistent handling for Throwables that are normally swallowed including always re-throwing certain Throwables (e.g. ThreadDeath). When waiting threads are interrupted, GOP, GKOP may leak capacity. Documentation for the close method in GenericObjectPool and GenericKeyedObjectPool incorrectly states that this method does not clear the pool. GenericObjectPool can block forever in borrowObject when the pool is exhausted and a newly created object fails validation. When borrowing an object if a new object is created but validate fails, the latch should not be returned to the queue as an exception will be thrown. Fix case where a thread could end up waiting indefinitely even if objects were available. Also fixes a couple of leaks in the internal processing object count that could lead to pool exhaustion. Handle the case where one key has reached maxActive but other keys have not. Prior to the fix for this issue, threads waiting on objects from keyed pools still having instances available could be blocked by a thread requesting an instance from an exhausted pool. Fix case where a thread could end up waiting indefinitely even if objects were available. Ensure that the GenericKeyedObjectPool idle object evictor does not visit the same instance more than once per eviction run. When exhausted action is set to WHEN_EXHAUSTED_BLOCK, maxwait is positive and client threads time out waiting for idle objects, capacity can be "leaked" from GenericObjectPools and GeneritCkeyedObjectPools. StackKeyedObjectPool.getNumActive() needs to be synchronized. Inconsistent synchronization in GenericObjectPool; constant fields should be final. GenericObjectPool not FIFO with respect to borrowing threads. _numActive > _maxActive under load Insufficient control over concurrent access to pooled objects by Evictor, client threads. Number of connections created has crossed more than maxActive. java.util.Timer in EvictionTimer does not recover from OutOfMemoryError in Evictor. Failed object creation may result in invalid active count in GKOP. Fixed constructor which was ignoring maxTotal parameter: GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int maxTotal, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) Changed StackKeyedObjectPool to discard stalest, not freshest, idle object when maxSleeping is reached. Allowed blocked threads in GenericObjectPool borrowObject to be interrupted. Fixes to address idle object eviction and LIFO/FIFO behavior reported in POOL-86. Made LIFO/FIFO behavior configurable for GenericObjectPool and GenericKeyedObjectPool, with default set back to LIFO (reverting to 1.2 behavior). Fixed GOP, GKOP evict method and added tests to ensure objects are visited in oldest-to-youngest order. Changed backing store for GOP, GKOP pools back to Commons Collections CursorableLinkedList (brought this class in, repackaged with package scope). Changed the default setting for Config.softMinEvictableIdleTimeMillis to GenericObjectPool.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS (was being incorrectly defaulted to DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS). Added a wrapper for the shared idle object eviction timer for all pools. The wrapper class wraps the Timer and keeps track of how many pools are using it. If no pools are using the timer, it is canceled. This prevents a thread being left running which, in application server environments, can lead to memory leaks and/or prevent applications from shutting down or reloading cleanly. Reduced synchronization in GenericObjectPool, GenericKeyedObjectPool. Factory method activations within synchronized blocks were causing performance problems in DBCP and other applications where factory methods could block. Fixes both POOL-93 and POOL-108. Made _testOnBorrow, _testOnReturn volatile and removed synchronization in associated getters and setters in GenericObjectPool, GenericKeyedObjectPool. Made getNumIdle synchronized in StackKeyedObjectPool. Fixed an error in the GenericKeyedObjectPool constructor that takes a Config instance as a parameter. The minIdle setting in the Config was being ignored by the constructor. Made behavior on instance validation failure consistent across pools, eliminating possible infinite loops in StackObjectPool, StackKeyedObjectPool, SoftReferenceObjectPool when factory fails to create valid objects. When no factory has been defined, addObject now throws IllegalStateExecption instead of NullPointerException for all pools. SoftReferenceObjectPool. Improved the accuracy of getNumIdle by "pruning" references to objects that have been garbage collected. GenericObjectPool, GenericKeyedObjectPool, SoftReferenceObjectPool, StackObjectPool. Eliminated IllegalStateExceptions when the following operations are attempted on a closed pool: getNumActive, getNumIdle, returnObject, invalidateObject. In each case, the operation is allowed to proceed, reporting the state of the pool that is being shut down, or destroying objects returning to the closed pool. StackObjectPool, SoftReferenceObjectPool, GenericKeyedObjectPool. Allowed borrowObject to continue (either examining additional idle instances or with makeObject) when an exception is encountered activating an idle object instead of propagating the exception to the client. Also made addObject propagate (not swallow) exceptions when passivating newly created instances. StackKeyedObjectPool. Added validation check for objects returned from borrowObject. BaseObjectPool, BaseKeyedObjectPool. Instead of throwing UnsupportedOperationException, the base class implementations of getNumIdle and getNumActive return negative values. The base implementation of close in BaseObjectPool no longer throws IllegalStateException when invoked on an already closed pool. A large number of bug fixes. See release notes for changes. GenericKeyedObjectPoolFactory Config Constructor is incorrect Not possible to extend GenericObjectPool.returnObject() without affecting addObject() A lot of corner cases were fixed Performance improvement by optimizing pool synchronization, the critical code paths were optimized by reducing pool synchronization but we also added more synchronization where needed New minIdle feature: the minimum number of objects allowed in the pool before the evictor thread (if active) spawns new objects. (Note no objects are created when: numActive + numIdle >= maxActive) New maxTotal feature: a cap on the total number of instances controlled by a pool. Only for GenericKeyedObjectPool where maxActive is a cap on the number of active instances from the pool (per key). UML Class and sequence diagrams See bugzilla for more changes No change log available. No change log available. commons-pool-rel-commons-pool-2.10.0/src/changes/release-notes.vm000066400000000000000000000114111405425132200247210ustar00rootroot00000000000000## 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} ${version} RELEASE NOTES The ${developmentTeam} is pleased to announce the release of ${project.name} ${version}. Apache Commons Pool provides an object-pooling API and a number of object pool implementations. Version 2 contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring. Version 2.10.x requires Java 8 or above. Version 2.9.x requires Java 8 or above. Version 2.8.x requires Java 8 or above. Version 2.7.x requires Java 8 or above. Version 2.6.x requires Java 7 or above. Version 2.5.x requires Java 7 or above. Version 2.0 requires 6 or above. No client code changes are required to migrate from versions 2.0-2.3 to version 2.4.3. Users of version 1.x should consult the migration guide on the Commons Pool web site. NOTE: The MBean interfaces (DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean and GenericKeyedObjectPoolMXBean) exist only to define the attributes and methods that will be made available via JMX. They must not be implemented by clients as they are subject to change between major, minor and patch version releases of Commons Pool. Clients that implement any of these interfaces may not, therefore, be able to upgrade to a new minor or patch release without requiring code changes. ## N.B. the available variables are described here: ## http://maven.apache.org/plugins/maven-changes-plugin/examples/using-a-custom-announcement-template.html ## ## Hack to get line breaks to work in release description. For this to work, each line break in the ## release description attribute in changes.xml needs to be preceded by a single space. #set( $desc = $release.description ) #set( $desc2 = $desc.replace($esc.newline, " ") ) #set( $d = $desc2.replace(" ",$esc.newline) ) ${d} ## set up indent sizes. Only change indent1 #set($props=${project.properties}) #set($jiralen=$props.get("commons.jira.id").length()) ## indent1 = POOL-nnnn: #set($blanklen=$jiralen+6)## +6 for "-nnnn:" ## must be at least as long as the longest JIRA id #set($blanks=" ") #set($indent1=$blanks.substring(0,$blanklen)) ## indent2 allows for issue wrapper #set($indent2="$indent1 ") ## #macro ( processaction ) ## Use replaceAll to fix up LF-only line ends on Windows. #set($action=$actionItem.getAction().replaceAll("\n"," ")) ## Fix up indentation for multi-line action descriptions #set($action=$action.replaceAll("(?m)^ +",$indent2)) #if ($actionItem.getIssue()) #set($issue="$actionItem.getIssue():") ## Pad shorter issue numbers #if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end #if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end #if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end #else #set($issue=$indent1) #end #if ($actionItem.getDueTo()) #set($dueto=" Thanks to $actionItem.getDueTo().") #else #set($dueto="") #end o $issue ${action}$dueto #set($action="") #set($issue="") #set($dueto="") #end ## #if ($release.getActions().size() == 0) No changes defined in this version. #else Changes in version ${version} include: #if ($release.getActions('add').size() !=0) New features: #foreach($actionItem in $release.getActions('add')) #processaction() #end #end #if ($release.getActions('fix').size() !=0) Fixed Bugs: #foreach($actionItem in $release.getActions('fix')) #processaction() #end #end #if ($release.getActions('update').size() !=0) Changes: #foreach($actionItem in $release.getActions('update')) #processaction() #end #end #if ($release.getActions('remove').size() !=0) Removed: #foreach($actionItem in $release.getActions('remove')) #processaction() #end #end ## End of main loop #end For complete information on ${project.name}, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache ${project.name} website: ${project.url} Download page: ${project.url}download_pool.cgi commons-pool-rel-commons-pool-2.10.0/src/main/000077500000000000000000000000001405425132200211255ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/000077500000000000000000000000001405425132200220465ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/000077500000000000000000000000001405425132200226355ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/000077500000000000000000000000001405425132200240565ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/000077500000000000000000000000001405425132200255315ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/000077500000000000000000000000001405425132200265645ustar00rootroot00000000000000BaseKeyedPooledObjectFactory.java000066400000000000000000000076741405425132200350440ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/* * 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.commons.pool2; /** * A base implementation of {@code KeyedPooledObjectFactory}. *

* All operations defined here are essentially no-op's. *

*

* This class is immutable, and therefore thread-safe. *

* * @see KeyedPooledObjectFactory * * @param The type of keys managed by this factory. * @param Type of element managed by this factory. * * @since 2.0 */ public abstract class BaseKeyedPooledObjectFactory extends BaseObject implements KeyedPooledObjectFactory { /** * Reinitialize an instance to be returned by the pool. *

* The default implementation is a no-op. *

* * @param key the key used when selecting the object * @param p a {@code PooledObject} wrapping the instance to be activated */ @Override public void activateObject(final K key, final PooledObject p) throws Exception { // The default implementation is a no-op. } /** * Create an instance that can be served by the pool. * * @param key the key used when constructing the object * @return an instance that can be served by the pool * * @throws Exception if there is a problem creating a new instance, * this will be propagated to the code requesting an object. */ public abstract V create(K key) throws Exception; /** * Destroy an instance no longer needed by the pool. *

* The default implementation is a no-op. *

* * @param key the key used when selecting the instance * @param p a {@code PooledObject} wrapping the instance to be destroyed */ @Override public void destroyObject(final K key, final PooledObject p) throws Exception { // The default implementation is a no-op. } @Override public PooledObject makeObject(final K key) throws Exception { return wrap(create(key)); } /** * Uninitialize an instance to be returned to the idle object pool. *

* The default implementation is a no-op. *

* * @param key the key used when selecting the object * @param p a {@code PooledObject} wrapping the instance to be passivated */ @Override public void passivateObject(final K key, final PooledObject p) throws Exception { // The default implementation is a no-op. } /** * Ensures that the instance is safe to be returned by the pool. *

* The default implementation always returns {@code true}. *

* * @param key the key used when selecting the object * @param p a {@code PooledObject} wrapping the instance to be validated * @return always {@code true} in the default implementation */ @Override public boolean validateObject(final K key, final PooledObject p) { return true; } /** * Wrap the provided instance with an implementation of * {@link PooledObject}. * * @param value the instance to wrap * * @return The provided instance, wrapped by a {@link PooledObject} */ public abstract PooledObject wrap(V value); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/BaseObject.java000066400000000000000000000030721405425132200314320ustar00rootroot00000000000000/* * 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.commons.pool2; /** * A base class for common functionality. * * @since 2.4.3 */ public abstract class BaseObject { @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append(getClass().getSimpleName()); builder.append(" ["); toStringAppendFields(builder); builder.append("]"); return builder.toString(); } /** * Used by sub-classes to include the fields defined by the sub-class in the * {@link #toString()} output. * * @param builder Field names and values are appended to this object */ protected void toStringAppendFields(final StringBuilder builder) { // do nothing by default, needed for b/w compatibility. } } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/BaseObjectPool.java000066400000000000000000000067431405425132200322740ustar00rootroot00000000000000/* * 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.commons.pool2; /** * A simple base implementation of {@link ObjectPool}. * Optional operations are implemented to either do nothing, return a value * indicating it is unsupported or throw {@link UnsupportedOperationException}. *

* This class is intended to be thread-safe. *

* * @param Type of element pooled in this pool. * * @since 2.0 */ public abstract class BaseObjectPool extends BaseObject implements ObjectPool { private volatile boolean closed = false; /** * Not supported in this base implementation. Subclasses should override * this behavior. * * @throws UnsupportedOperationException if the pool does not implement this * method */ @Override public void addObject() throws Exception, UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Throws an {@code IllegalStateException} when this pool has been * closed. * * @throws IllegalStateException when this pool has been closed. * * @see #isClosed() */ protected final void assertOpen() throws IllegalStateException { if (isClosed()) { throw new IllegalStateException("Pool not open"); } } @Override public abstract T borrowObject() throws Exception; /** * Not supported in this base implementation. * * @throws UnsupportedOperationException if the pool does not implement this * method */ @Override public void clear() throws Exception, UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * {@inheritDoc} *

* This affects the behavior of {@code isClosed} and * {@code assertOpen}. *

*/ @Override public void close() { closed = true; } /** * Not supported in this base implementation. * * @return a negative value. */ @Override public int getNumActive() { return -1; } /** * Not supported in this base implementation. * * @return a negative value. */ @Override public int getNumIdle() { return -1; } @Override public abstract void invalidateObject(T obj) throws Exception; /** * Has this pool instance been closed. * * @return {@code true} when this pool has been closed. */ public final boolean isClosed() { return closed; } @Override public abstract void returnObject(T obj) throws Exception; @Override protected void toStringAppendFields(final StringBuilder builder) { builder.append("closed="); builder.append(closed); } } BasePooledObjectFactory.java000066400000000000000000000056721405425132200340560ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/* * 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.commons.pool2; /** * A base implementation of {@code PoolableObjectFactory}. *

* All operations defined here are essentially no-op's. *

* This class is immutable, and therefore thread-safe * * @param Type of element managed in this factory. * * @see PooledObjectFactory * @see BaseKeyedPooledObjectFactory * * @since 2.0 */ public abstract class BasePooledObjectFactory extends BaseObject implements PooledObjectFactory { /** * No-op. * * @param p ignored */ @Override public void activateObject(final PooledObject p) throws Exception { // The default implementation is a no-op. } /** * Creates an object instance, to be wrapped in a {@link PooledObject}. *

This method must support concurrent, multi-threaded * activation.

* * @return an instance to be served by the pool * * @throws Exception if there is a problem creating a new instance, * this will be propagated to the code requesting an object. */ public abstract T create() throws Exception; /** * No-op. * * @param p ignored */ @Override public void destroyObject(final PooledObject p) throws Exception { // The default implementation is a no-op. } @Override public PooledObject makeObject() throws Exception { return wrap(create()); } /** * No-op. * * @param p ignored */ @Override public void passivateObject(final PooledObject p) throws Exception { // The default implementation is a no-op. } /** * This implementation always returns {@code true}. * * @param p ignored * * @return {@code true} */ @Override public boolean validateObject(final PooledObject p) { return true; } /** * Wrap the provided instance with an implementation of * {@link PooledObject}. * * @param obj the instance to wrap * * @return The provided instance, wrapped by a {@link PooledObject} */ public abstract PooledObject wrap(T obj); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/DestroyMode.java000066400000000000000000000022001405425132200316570ustar00rootroot00000000000000/* * 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.commons.pool2; /** * Destroy context provided to object factories via destroyObject methods. Values provide information about why the pool * is asking for a pooled object to be destroyed. * * @since 2.9.0 */ public enum DestroyMode { /** Normal destroy */ NORMAL, /** Destroy abandoned object */ ABANDONED } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/KeyedObjectPool.java000066400000000000000000000307441405425132200324610ustar00rootroot00000000000000/* * 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.commons.pool2; import java.io.Closeable; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; /** * A "keyed" pooling interface. *

* A keyed pool maintains a pool of instances for each key value. *

*

* Example of use: *

*
 Object obj = null;
 * Object key = "Key";
 *
 * try {
 *     obj = pool.borrowObject(key);
 *     //...use the object...
 * } catch(Exception e) {
 *     // invalidate the object
 *     pool.invalidateObject(key, obj);
 *     // do not return the object to the pool twice
 *     obj = null;
 * } finally {
 *     // make sure the object is returned to the pool
 *     if(null != obj) {
 *         pool.returnObject(key, obj);
 *     }
 * }
*

* {@link KeyedObjectPool} implementations may choose to store at most * one instance per key value, or may choose to maintain a pool of instances * for each key (essentially creating a {@link java.util.Map Map} of * {@link ObjectPool pools}). *

*

* See {@link org.apache.commons.pool2.impl.GenericKeyedObjectPool * GenericKeyedObjectPool} for an implementation. *

* * @param The type of keys maintained by this pool. * @param Type of element pooled in this pool. * * @see KeyedPooledObjectFactory * @see ObjectPool * @see org.apache.commons.pool2.impl.GenericKeyedObjectPool GenericKeyedObjectPool * * @since 2.0 */ public interface KeyedObjectPool extends Closeable { /** * Create an object using the {@link KeyedPooledObjectFactory factory} or * other implementation dependent mechanism, passivate it, and then place it * in the idle object pool. {@code addObject} is useful for * "pre-loading" a pool with idle objects (Optional operation). * * @param key the key a new instance should be added to * * @throws Exception * when {@link KeyedPooledObjectFactory#makeObject} fails. * @throws IllegalStateException * after {@link #close} has been called on this pool. * @throws UnsupportedOperationException * when this pool cannot add new idle objects. */ void addObject(K key) throws Exception, IllegalStateException, UnsupportedOperationException; /** * Calls {@link KeyedObjectPool#addObject(Object)} with each * key in {@code keys} for {@code count} number of times. This has * the same effect as calling {@link #addObjects(Object, int)} * for each key in the {@code keys} collection. * * @param keys * {@link Collection} of keys to add objects for. * @param count * the number of idle objects to add for each {@code key}. * @throws Exception * when {@link KeyedObjectPool#addObject(Object)} fails. * @throws IllegalArgumentException * when {@code keyedPool}, {@code keys}, or any value * in {@code keys} is {@code null}. * @see #addObjects(Object, int) */ default void addObjects(final Collection keys, final int count) throws Exception, IllegalArgumentException { if (keys == null) { throw new IllegalArgumentException(PoolUtils.MSG_NULL_KEYS); } final Iterator iter = keys.iterator(); while (iter.hasNext()) { addObjects(iter.next(), count); } } /** * Calls {@link KeyedObjectPool#addObject(Object)} * {@code key} {@code count} number of times. * * @param key * the key to add objects for. * @param count * the number of idle objects to add for {@code key}. * @throws Exception * when {@link KeyedObjectPool#addObject(Object)} fails. * @throws IllegalArgumentException * when {@code key} is {@code null}. * @since 2.8.0 */ default void addObjects(final K key, final int count) throws Exception, IllegalArgumentException { if (key == null) { throw new IllegalArgumentException(PoolUtils.MSG_NULL_KEY); } for (int i = 0; i < count; i++) { addObject(key); } } /** * Borrows an instance from this pool for the specified {@code key}. *

* Instances returned from this method will have been either newly created * with {@link KeyedPooledObjectFactory#makeObject makeObject} or will be * a previously idle object and have been activated with * {@link KeyedPooledObjectFactory#activateObject activateObject} and then * (optionally) validated with * {@link KeyedPooledObjectFactory#validateObject validateObject}. *

*

* By contract, clients must return the borrowed object * using {@link #returnObject returnObject}, * {@link #invalidateObject invalidateObject}, or a related method as * defined in an implementation or sub-interface, using a {@code key} * that is {@link Object#equals equivalent} to the one used to borrow the * instance in the first place. *

*

* The behavior of this method when the pool has been exhausted is not * strictly specified (although it may be specified by implementations). *

* * @param key the key used to obtain the object * * @return an instance from this pool. * * @throws IllegalStateException * after {@link #close close} has been called on this pool * @throws Exception * when {@link KeyedPooledObjectFactory#makeObject * makeObject} throws an exception * @throws NoSuchElementException * when the pool is exhausted and cannot or will not return * another instance */ V borrowObject(K key) throws Exception, NoSuchElementException, IllegalStateException; /** * Clears the pool, removing all pooled instances (optional operation). * * @throws UnsupportedOperationException when this implementation doesn't * support the operation * * @throws Exception if the pool cannot be cleared */ void clear() throws Exception, UnsupportedOperationException; /** * Clears the specified pool, removing all pooled instances corresponding to * the given {@code key} (optional operation). * * @param key the key to clear * * @throws UnsupportedOperationException when this implementation doesn't * support the operation * * @throws Exception if the key cannot be cleared */ void clear(K key) throws Exception, UnsupportedOperationException; /** * Close this pool, and free any resources associated with it. *

* Calling {@link #addObject addObject} or * {@link #borrowObject borrowObject} after invoking this method on a pool * will cause them to throw an {@link IllegalStateException}. *

*

* Implementations should silently fail if not all resources can be freed. *

*/ @Override void close(); /** * Gets the total number of instances currently borrowed from this pool but * not yet returned. Returns a negative value if this information is not * available. * @return the total number of instances currently borrowed from this pool but * not yet returned. */ int getNumActive(); /** * Gets the number of instances currently borrowed from but not yet * returned to the pool corresponding to the given {@code key}. * Returns a negative value if this information is not available. * * @param key the key to query * @return the number of instances currently borrowed from but not yet * returned to the pool corresponding to the given {@code key}. */ int getNumActive(K key); /** * Gets the total number of instances currently idle in this pool. * Returns a negative value if this information is not available. * @return the total number of instances currently idle in this pool. */ int getNumIdle(); /** * Gets the number of instances corresponding to the given * {@code key} currently idle in this pool. Returns a negative value if * this information is not available. * * @param key the key to query * @return the number of instances corresponding to the given * {@code key} currently idle in this pool. */ int getNumIdle(K key); /** * Invalidates an object from the pool. *

* By contract, {@code obj} must have been obtained * using {@link #borrowObject borrowObject} or a related method as defined * in an implementation or sub-interface using a {@code key} that is * equivalent to the one used to borrow the {@code Object} in the first * place. *

*

* This method should be used when an object that has been borrowed is * determined (due to an exception or other problem) to be invalid. *

* * @param key the key used to obtain the object * @param obj a {@link #borrowObject borrowed} instance to be returned. * * @throws Exception if the instance cannot be invalidated */ void invalidateObject(K key, V obj) throws Exception; /** * Invalidates an object from the pool, using the provided * {@link DestroyMode}. *

* By contract, {@code obj} must have been obtained * using {@link #borrowObject borrowObject} or a related method as defined * in an implementation or sub-interface using a {@code key} that is * equivalent to the one used to borrow the {@code Object} in the first * place. *

*

* This method should be used when an object that has been borrowed is * determined (due to an exception or other problem) to be invalid. *

* * @param key the key used to obtain the object * @param obj a {@link #borrowObject borrowed} instance to be returned. * @param mode destroy activation context provided to the factory * * @throws Exception if the instance cannot be invalidated * @since 2.9.0 */ default void invalidateObject(final K key, final V obj, final DestroyMode mode) throws Exception { invalidateObject(key, obj); } /** * Return an instance to the pool. By contract, {@code obj} * must have been obtained using * {@link #borrowObject borrowObject} or a related method as defined in an * implementation or sub-interface using a {@code key} that is * equivalent to the one used to borrow the instance in the first place. * * @param key the key used to obtain the object * @param obj a {@link #borrowObject borrowed} instance to be returned. * * @throws IllegalStateException * if an attempt is made to return an object to the pool that * is in any state other than allocated (i.e. borrowed). * Attempting to return an object more than once or attempting * to return an object that was never borrowed from the pool * will trigger this exception. * * @throws Exception if an instance cannot be returned to the pool */ void returnObject(K key, V obj) throws Exception; } KeyedPooledObjectFactory.java000066400000000000000000000150441405425132200342370ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/* * 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.commons.pool2; /** * An interface defining life-cycle methods for * instances to be served by a {@link KeyedObjectPool}. *

* By contract, when an {@link KeyedObjectPool} * delegates to a {@link KeyedPooledObjectFactory}, *

*
    *
  1. * {@link #makeObject} is called whenever a new instance is needed. *
  2. *
  3. * {@link #activateObject} is invoked on every instance that has been * {@link #passivateObject passivated} before it is * {@link KeyedObjectPool#borrowObject borrowed} from the pool. *
  4. *
  5. * {@link #validateObject} may be invoked on {@link #activateObject activated} * instances to make sure they can be * {@link KeyedObjectPool#borrowObject borrowed} from the pool. * {@code validateObject} may also be used to test an * instance being {@link KeyedObjectPool#returnObject returned} to the pool * before it is {@link #passivateObject passivated}. It will only be invoked * on an activated instance. *
  6. *
  7. * {@link #passivateObject passivateObject} * is invoked on every instance when it is returned to the pool. *
  8. *
  9. * {@link #destroyObject destroyObject} * is invoked on every instance when it is being "dropped" from the * pool (whether due to the response from {@code validateObject}, * or for reasons specific to the pool implementation.) There is no * guarantee that the instance being destroyed will * be considered active, passive or in a generally consistent state. *
  10. *
* {@link KeyedPooledObjectFactory} must be thread-safe. The only promise * an {@link KeyedObjectPool} makes is that the same instance of an object will * not be passed to more than one method of a * {@code KeyedPoolableObjectFactory} at a time. *

* While clients of a {@link KeyedObjectPool} borrow and return instances of * the underlying value type V, the factory methods act on instances of * {@link PooledObject PooledObject<V>}. These are the object wrappers that * pools use to track and maintain state informations about the objects that * they manage. *

* * @see KeyedObjectPool * @see BaseKeyedPooledObjectFactory * * @param The type of keys managed by this factory. * @param Type of element managed by this factory. * * @since 2.0 */ public interface KeyedPooledObjectFactory { /** * Reinitialize an instance to be returned by the pool. * * @param key the key used when selecting the object * @param p a {@code PooledObject} wrapping the instance to be activated * * @throws Exception if there is a problem activating {@code obj}, * this exception may be swallowed by the pool. * * @see #destroyObject */ void activateObject(K key, PooledObject p) throws Exception; /** * Destroy an instance no longer needed by the pool. *

* It is important for implementations of this method to be aware that there * is no guarantee about what state {@code obj} will be in and the * implementation should be prepared to handle unexpected errors. *

*

* Also, an implementation must take in to consideration that instances lost * to the garbage collector may never be destroyed. *

* * @param key the key used when selecting the instance * @param p a {@code PooledObject} wrapping the instance to be destroyed * * @throws Exception should be avoided as it may be swallowed by * the pool implementation. * * @see #validateObject * @see KeyedObjectPool#invalidateObject */ void destroyObject(K key, PooledObject p) throws Exception; /** * Destroy an instance no longer needed by the pool, using the provided {@link DestroyMode}. * * @param key the key used when selecting the instance * @param p a {@code PooledObject} wrapping the instance to be destroyed * @param mode DestroyMode providing context to the factory * * @throws Exception should be avoided as it may be swallowed by * the pool implementation. * * @see #validateObject * @see KeyedObjectPool#invalidateObject * @see #destroyObject(Object, PooledObject) * @see DestroyMode * @since 2.9.0 */ default void destroyObject(final K key, final PooledObject p, final DestroyMode mode) throws Exception { destroyObject(key, p); } /** * Create an instance that can be served by the pool and * wrap it in a {@link PooledObject} to be managed by the pool. * * @param key the key used when constructing the object * * @return a {@code PooledObject} wrapping an instance that can * be served by the pool. * * @throws Exception if there is a problem creating a new instance, * this will be propagated to the code requesting an object. */ PooledObject makeObject(K key) throws Exception; /** * Uninitialize an instance to be returned to the idle object pool. * * @param key the key used when selecting the object * @param p a {@code PooledObject} wrapping the instance to be passivated * * @throws Exception if there is a problem passivating {@code obj}, * this exception may be swallowed by the pool. * * @see #destroyObject */ void passivateObject(K key, PooledObject p) throws Exception; /** * Ensures that the instance is safe to be returned by the pool. * * @param key the key used when selecting the object * @param p a {@code PooledObject} wrapping the instance to be validated * * @return {@code false} if {@code obj} is not valid and should * be dropped from the pool, {@code true} otherwise. */ boolean validateObject(K key, PooledObject p); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/ObjectPool.java000066400000000000000000000210431405425132200314670ustar00rootroot00000000000000/* * 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.commons.pool2; import java.io.Closeable; import java.util.NoSuchElementException; /** * A pooling simple interface. *

* Example of use: *

*
 Object obj = null;
 *
 * try {
 *     obj = pool.borrowObject();
 *     try {
 *         //...use the object...
 *     } catch(Exception e) {
 *         // invalidate the object
 *         pool.invalidateObject(obj);
 *         // do not return the object to the pool twice
 *         obj = null;
 *     } finally {
 *         // make sure the object is returned to the pool
 *         if(null != obj) {
 *             pool.returnObject(obj);
 *        }
 *     }
 * } catch(Exception e) {
 *       // failed to borrow an object
 * }
*

* See {@link BaseObjectPool} for a simple base implementation. *

* * @param Type of element pooled in this pool. * * @see PooledObjectFactory * @see KeyedObjectPool * @see BaseObjectPool * * @since 2.0 */ public interface ObjectPool extends Closeable { /** * Creates an object using the {@link PooledObjectFactory factory} or other * implementation dependent mechanism, passivate it, and then place it in * the idle object pool. {@code addObject} is useful for "pre-loading" * a pool with idle objects. (Optional operation). * * @throws Exception * when {@link PooledObjectFactory#makeObject} fails. * @throws IllegalStateException * after {@link #close} has been called on this pool. * @throws UnsupportedOperationException * when this pool cannot add new idle objects. */ void addObject() throws Exception, IllegalStateException, UnsupportedOperationException; /** * Calls {@link ObjectPool#addObject()} {@code count} * number of times. * * @param count * the number of idle objects to add. * @throws Exception * when {@link ObjectPool#addObject()} fails. * @since 2.8.0 */ default void addObjects(final int count) throws Exception { for (int i = 0; i < count; i++) { addObject(); } } /** * Borrows an instance from this pool. *

* Instances returned from this method will have been either newly created * with {@link PooledObjectFactory#makeObject} or will be a previously * idle object and have been activated with * {@link PooledObjectFactory#activateObject} and then validated with * {@link PooledObjectFactory#validateObject}. *

*

* By contract, clients must return the borrowed instance * using {@link #returnObject}, {@link #invalidateObject}, or a related * method as defined in an implementation or sub-interface. *

*

* The behavior of this method when the pool has been exhausted * is not strictly specified (although it may be specified by * implementations). *

* * @return an instance from this pool. * * @throws IllegalStateException * after {@link #close close} has been called on this pool. * @throws Exception * when {@link PooledObjectFactory#makeObject} throws an * exception. * @throws NoSuchElementException * when the pool is exhausted and cannot or will not return * another instance. */ T borrowObject() throws Exception, NoSuchElementException, IllegalStateException; /** * Clears any objects sitting idle in the pool, releasing any associated * resources (optional operation). Idle objects cleared must be * {@link PooledObjectFactory#destroyObject(PooledObject)}. * * @throws UnsupportedOperationException * if this implementation does not support the operation * * @throws Exception if the pool cannot be cleared */ void clear() throws Exception, UnsupportedOperationException; /** * Closes this pool, and free any resources associated with it. *

* Calling {@link #addObject} or {@link #borrowObject} after invoking this * method on a pool will cause them to throw an {@link IllegalStateException}. *

*

* Implementations should silently fail if not all resources can be freed. *

*/ @Override void close(); /** * Gets the number of instances currently borrowed from this pool. Returns * a negative value if this information is not available. * @return the number of instances currently borrowed from this pool. */ int getNumActive(); /** * Gets the number of instances currently idle in this pool. This may be * considered an approximation of the number of objects that can be * {@link #borrowObject borrowed} without creating any new instances. * Returns a negative value if this information is not available. * @return the number of instances currently idle in this pool. */ int getNumIdle(); /** * Invalidates an object from the pool. *

* By contract, {@code obj} must have been obtained * using {@link #borrowObject} or a related method as defined in an * implementation or sub-interface. *

*

* This method should be used when an object that has been borrowed is * determined (due to an exception or other problem) to be invalid. *

* * @param obj a {@link #borrowObject borrowed} instance to be disposed. * * @throws Exception if the instance cannot be invalidated */ void invalidateObject(T obj) throws Exception; /** * Invalidates an object from the pool, using the provided * {@link DestroyMode} *

* By contract, {@code obj} must have been obtained * using {@link #borrowObject} or a related method as defined in an * implementation or sub-interface. *

*

* This method should be used when an object that has been borrowed is * determined (due to an exception or other problem) to be invalid. *

* * @param obj a {@link #borrowObject borrowed} instance to be disposed. * @param mode destroy activation context provided to the factory * * @throws Exception if the instance cannot be invalidated * @since 2.9.0 */ default void invalidateObject(final T obj, final DestroyMode mode) throws Exception { invalidateObject(obj); } /** * Returns an instance to the pool. By contract, {@code obj} * must have been obtained using {@link #borrowObject()} or * a related method as defined in an implementation or sub-interface. * * @param obj a {@link #borrowObject borrowed} instance to be returned. * * @throws IllegalStateException * if an attempt is made to return an object to the pool that * is in any state other than allocated (i.e. borrowed). * Attempting to return an object more than once or attempting * to return an object that was never borrowed from the pool * will trigger this exception. * * @throws Exception if an instance cannot be returned to the pool */ void returnObject(T obj) throws Exception; } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/PoolUtils.java000066400000000000000000001733551405425132200313770ustar00rootroot00000000000000/* * 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.commons.pool2; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; /** * This class consists exclusively of static methods that operate on or return * ObjectPool or KeyedObjectPool related interfaces. * * @since 2.0 */ public final class PoolUtils { /** * Encapsulate the logic for when the next poolable object should be * discarded. Each time update is called, the next time to shrink is * recomputed, based on the float factor, number of idle instances in the * pool and high water mark. Float factor is assumed to be between 0 and 1. * Values closer to 1 cause less frequent erosion events. Erosion event * timing also depends on numIdle. When this value is relatively high (close * to previously established high water mark), erosion occurs more * frequently. */ private static final class ErodingFactor { /** Determines frequency of "erosion" events */ private final float factor; /** Time of next shrink event */ private transient volatile long nextShrinkMillis; /** High water mark - largest numIdle encountered */ private transient volatile int idleHighWaterMark; /** * Creates a new ErodingFactor with the given erosion factor. * * @param factor * erosion factor */ public ErodingFactor(final float factor) { this.factor = factor; nextShrinkMillis = System.currentTimeMillis() + (long) (900000 * factor); // now // + // 15 // min // * // factor idleHighWaterMark = 1; } /** * Gets the time of the next erosion event. * * @return next shrink time */ public long getNextShrink() { return nextShrinkMillis; } /** * {@inheritDoc} */ @Override public String toString() { return "ErodingFactor{" + "factor=" + factor + ", idleHighWaterMark=" + idleHighWaterMark + '}'; } /** * Updates internal state using the supplied time and numIdle. * * @param nowMillis * current time * @param numIdle * number of idle elements in the pool */ public void update(final long nowMillis, final int numIdle) { final int idle = Math.max(0, numIdle); idleHighWaterMark = Math.max(idle, idleHighWaterMark); final float maxInterval = 15f; final float minutes = maxInterval + ((1f - maxInterval) / idleHighWaterMark) * idle; nextShrinkMillis = nowMillis + (long) (minutes * 60000f * factor); } } /** * Decorates a keyed object pool, adding "eroding" behavior. Based on the * configured erosion factor, objects returning to the pool * may be invalidated instead of being added to idle capacity. * * @param object pool key type * @param object pool value type */ private static class ErodingKeyedObjectPool implements KeyedObjectPool { /** Underlying pool */ private final KeyedObjectPool keyedPool; /** Erosion factor */ private final ErodingFactor erodingFactor; /** * Creates an ErodingObjectPool wrapping the given pool using the * specified erosion factor. * * @param keyedPool * underlying pool - must not be null * @param erodingFactor * erosion factor - determines the frequency of erosion * events * @see #erodingFactor */ protected ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final ErodingFactor erodingFactor) { if (keyedPool == null) { throw new IllegalArgumentException( MSG_NULL_KEYED_POOL); } this.keyedPool = keyedPool; this.erodingFactor = erodingFactor; } /** * Creates an ErodingObjectPool wrapping the given pool using the * specified erosion factor. * * @param keyedPool * underlying pool * @param factor * erosion factor - determines the frequency of erosion * events * @see #erodingFactor */ public ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final float factor) { this(keyedPool, new ErodingFactor(factor)); } /** * {@inheritDoc} */ @Override public void addObject(final K key) throws Exception, IllegalStateException, UnsupportedOperationException { keyedPool.addObject(key); } /** * {@inheritDoc} */ @Override public V borrowObject(final K key) throws Exception, NoSuchElementException, IllegalStateException { return keyedPool.borrowObject(key); } /** * {@inheritDoc} */ @Override public void clear() throws Exception, UnsupportedOperationException { keyedPool.clear(); } /** * {@inheritDoc} */ @Override public void clear(final K key) throws Exception, UnsupportedOperationException { keyedPool.clear(key); } /** * {@inheritDoc} */ @Override public void close() { try { keyedPool.close(); } catch (final Exception e) { // swallowed } } /** * Gets the eroding factor for the given key * * @param key * key * @return eroding factor for the given keyed pool */ protected ErodingFactor getErodingFactor(final K key) { return erodingFactor; } /** * Gets the underlying pool * * @return the keyed pool that this ErodingKeyedObjectPool wraps */ protected KeyedObjectPool getKeyedPool() { return keyedPool; } /** * {@inheritDoc} */ @Override public int getNumActive() { return keyedPool.getNumActive(); } /** * {@inheritDoc} */ @Override public int getNumActive(final K key) { return keyedPool.getNumActive(key); } /** * {@inheritDoc} */ @Override public int getNumIdle() { return keyedPool.getNumIdle(); } /** * {@inheritDoc} */ @Override public int getNumIdle(final K key) { return keyedPool.getNumIdle(key); } /** * {@inheritDoc} */ @Override public void invalidateObject(final K key, final V obj) { try { keyedPool.invalidateObject(key, obj); } catch (final Exception e) { // swallowed } } /** * Returns obj to the pool, unless erosion is triggered, in which case * obj is invalidated. Erosion is triggered when there are idle * instances in the pool associated with the given key and more than the * configured {@link #erodingFactor erosion factor} time has elapsed * since the last returnObject activation. * * @param obj * object to return or invalidate * @param key * key * @see #erodingFactor */ @Override public void returnObject(final K key, final V obj) throws Exception { boolean discard = false; final long nowMillis = System.currentTimeMillis(); final ErodingFactor factor = getErodingFactor(key); synchronized (keyedPool) { if (factor.getNextShrink() < nowMillis) { final int numIdle = getNumIdle(key); if (numIdle > 0) { discard = true; } factor.update(nowMillis, numIdle); } } try { if (discard) { keyedPool.invalidateObject(key, obj); } else { keyedPool.returnObject(key, obj); } } catch (final Exception e) { // swallowed } } /** * {@inheritDoc} */ @Override public String toString() { return "ErodingKeyedObjectPool{" + "factor=" + erodingFactor + ", keyedPool=" + keyedPool + '}'; } } /** * Decorates an object pool, adding "eroding" behavior. Based on the * configured {@link #factor erosion factor}, objects returning to the pool * may be invalidated instead of being added to idle capacity. * * @param type of objects in the pool */ private static class ErodingObjectPool implements ObjectPool { /** Underlying object pool */ private final ObjectPool pool; /** Erosion factor */ private final ErodingFactor factor; /** * Creates an ErodingObjectPool wrapping the given pool using the * specified erosion factor. * * @param pool * underlying pool * @param factor * erosion factor - determines the frequency of erosion * events * @see #factor */ public ErodingObjectPool(final ObjectPool pool, final float factor) { this.pool = pool; this.factor = new ErodingFactor(factor); } /** * {@inheritDoc} */ @Override public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException { pool.addObject(); } /** * {@inheritDoc} */ @Override public T borrowObject() throws Exception, NoSuchElementException, IllegalStateException { return pool.borrowObject(); } /** * {@inheritDoc} */ @Override public void clear() throws Exception, UnsupportedOperationException { pool.clear(); } /** * {@inheritDoc} */ @Override public void close() { try { pool.close(); } catch (final Exception e) { // swallowed } } /** * {@inheritDoc} */ @Override public int getNumActive() { return pool.getNumActive(); } /** * {@inheritDoc} */ @Override public int getNumIdle() { return pool.getNumIdle(); } /** * {@inheritDoc} */ @Override public void invalidateObject(final T obj) { try { pool.invalidateObject(obj); } catch (final Exception e) { // swallowed } } /** * Returns * Gets obj to the pool, unless erosion is triggered, in which case * obj is invalidated. Erosion is triggered when there are idle * instances in the pool and more than the {@link #factor erosion * factor}-determined time has elapsed since the last returnObject * activation. * * @param obj * object to return or invalidate * @see #factor */ @Override public void returnObject(final T obj) { boolean discard = false; final long nowMillis = System.currentTimeMillis(); synchronized (pool) { if (factor.getNextShrink() < nowMillis) { // XXX: Pool 3: move test // out of sync block final int numIdle = pool.getNumIdle(); if (numIdle > 0) { discard = true; } factor.update(nowMillis, numIdle); } } try { if (discard) { pool.invalidateObject(obj); } else { pool.returnObject(obj); } } catch (final Exception e) { // swallowed } } /** * {@inheritDoc} */ @Override public String toString() { return "ErodingObjectPool{" + "factor=" + factor + ", pool=" + pool + '}'; } } /** * Extends ErodingKeyedObjectPool to allow erosion to take place on a * per-key basis. Timing of erosion events is tracked separately for * separate keyed pools. * * @param object pool key type * @param object pool value type */ private static final class ErodingPerKeyKeyedObjectPool extends ErodingKeyedObjectPool { /** Erosion factor - same for all pools */ private final float factor; /** Map of ErodingFactor instances keyed on pool keys */ private final Map factors = Collections.synchronizedMap(new HashMap<>()); /** * Creates a new ErordingPerKeyKeyedObjectPool decorating the given keyed * pool with the specified erosion factor. * * @param keyedPool * underlying keyed pool * @param factor * erosion factor */ public ErodingPerKeyKeyedObjectPool( final KeyedObjectPool keyedPool, final float factor) { super(keyedPool, null); this.factor = factor; } /** * {@inheritDoc} */ @Override protected ErodingFactor getErodingFactor(final K key) { ErodingFactor eFactor = factors.get(key); // this may result in two ErodingFactors being created for a key // since they are small and cheap this is okay. if (eFactor == null) { eFactor = new ErodingFactor(this.factor); factors.put(key, eFactor); } return eFactor; } /** * {@inheritDoc} */ @SuppressWarnings("resource") // getKeyedPool(): ivar access @Override public String toString() { return "ErodingPerKeyKeyedObjectPool{" + "factor=" + factor + ", keyedPool=" + getKeyedPool() + '}'; } } /** * Timer task that adds objects to the pool until the number of idle * instances for the given key reaches the configured minIdle. Note that * this is not the same as the pool's minIdle setting. * * @param object pool key type * @param object pool value type */ private static final class KeyedObjectPoolMinIdleTimerTask extends TimerTask { /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */ private final int minIdle; /** Key to ensure minIdle for */ private final K key; /** Keyed object pool */ private final KeyedObjectPool keyedPool; /** * Creates a new KeyedObjecPoolMinIdleTimerTask. * * @param keyedPool * keyed object pool * @param key * key to ensure minimum number of idle instances * @param minIdle * minimum number of idle instances * @throws IllegalArgumentException * if the key is null */ KeyedObjectPoolMinIdleTimerTask(final KeyedObjectPool keyedPool, final K key, final int minIdle) throws IllegalArgumentException { if (keyedPool == null) { throw new IllegalArgumentException( MSG_NULL_KEYED_POOL); } this.keyedPool = keyedPool; this.key = key; this.minIdle = minIdle; } /** * {@inheritDoc} */ @Override public void run() { boolean success = false; try { if (keyedPool.getNumIdle(key) < minIdle) { keyedPool.addObject(key); } success = true; } catch (final Exception e) { cancel(); } finally { // detect other types of Throwable and cancel this Timer if (!success) { cancel(); } } } /** * {@inheritDoc} */ @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("KeyedObjectPoolMinIdleTimerTask"); sb.append("{minIdle=").append(minIdle); sb.append(", key=").append(key); sb.append(", keyedPool=").append(keyedPool); sb.append('}'); return sb.toString(); } } /** * Timer task that adds objects to the pool until the number of idle * instances reaches the configured minIdle. Note that this is not the same * as the pool's minIdle setting. * * @param type of objects in the pool */ private static final class ObjectPoolMinIdleTimerTask extends TimerTask { /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */ private final int minIdle; /** Object pool */ private final ObjectPool pool; /** * Create a new ObjectPoolMinIdleTimerTask for the given pool with the * given minIdle setting. * * @param pool * object pool * @param minIdle * number of idle instances to maintain * @throws IllegalArgumentException * if the pool is null */ ObjectPoolMinIdleTimerTask(final ObjectPool pool, final int minIdle) throws IllegalArgumentException { if (pool == null) { throw new IllegalArgumentException(MSG_NULL_POOL); } this.pool = pool; this.minIdle = minIdle; } /** * {@inheritDoc} */ @Override public void run() { boolean success = false; try { if (pool.getNumIdle() < minIdle) { pool.addObject(); } success = true; } catch (final Exception e) { cancel(); } finally { // detect other types of Throwable and cancel this Timer if (!success) { cancel(); } } } /** * {@inheritDoc} */ @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("ObjectPoolMinIdleTimerTask"); sb.append("{minIdle=").append(minIdle); sb.append(", pool=").append(pool); sb.append('}'); return sb.toString(); } } /** * A synchronized (thread-safe) KeyedObjectPool backed by the specified * KeyedObjectPool. *

* Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. Wrapping a pool that {@link #wait() waits} for poolable * objects to be returned before allowing another one to be borrowed with * another layer of synchronization will cause liveliness issues or a * deadlock. *

* * @param object pool key type * @param object pool value type */ private static final class SynchronizedKeyedObjectPool implements KeyedObjectPool { /** * Object whose monitor is used to synchronize methods on the wrapped * pool. */ private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); /** Underlying object pool */ private final KeyedObjectPool keyedPool; /** * Creates a new SynchronizedKeyedObjectPool wrapping the given pool * * @param keyedPool * KeyedObjectPool to wrap * @throws IllegalArgumentException * if keyedPool is null */ SynchronizedKeyedObjectPool(final KeyedObjectPool keyedPool) throws IllegalArgumentException { if (keyedPool == null) { throw new IllegalArgumentException( MSG_NULL_KEYED_POOL); } this.keyedPool = keyedPool; } /** * {@inheritDoc} */ @Override public void addObject(final K key) throws Exception, IllegalStateException, UnsupportedOperationException { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { keyedPool.addObject(key); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public V borrowObject(final K key) throws Exception, NoSuchElementException, IllegalStateException { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { return keyedPool.borrowObject(key); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void clear() throws Exception, UnsupportedOperationException { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { keyedPool.clear(); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void clear(final K key) throws Exception, UnsupportedOperationException { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { keyedPool.clear(key); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void close() { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { keyedPool.close(); } catch (final Exception e) { // swallowed as of Pool 2 } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public int getNumActive() { final ReadLock readLock = readWriteLock.readLock(); readLock.lock(); try { return keyedPool.getNumActive(); } finally { readLock.unlock(); } } /** * {@inheritDoc} */ @Override public int getNumActive(final K key) { final ReadLock readLock = readWriteLock.readLock(); readLock.lock(); try { return keyedPool.getNumActive(key); } finally { readLock.unlock(); } } /** * {@inheritDoc} */ @Override public int getNumIdle() { final ReadLock readLock = readWriteLock.readLock(); readLock.lock(); try { return keyedPool.getNumIdle(); } finally { readLock.unlock(); } } /** * {@inheritDoc} */ @Override public int getNumIdle(final K key) { final ReadLock readLock = readWriteLock.readLock(); readLock.lock(); try { return keyedPool.getNumIdle(key); } finally { readLock.unlock(); } } /** * {@inheritDoc} */ @Override public void invalidateObject(final K key, final V obj) { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { keyedPool.invalidateObject(key, obj); } catch (final Exception e) { // swallowed as of Pool 2 } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void returnObject(final K key, final V obj) { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { keyedPool.returnObject(key, obj); } catch (final Exception e) { // swallowed } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("SynchronizedKeyedObjectPool"); sb.append("{keyedPool=").append(keyedPool); sb.append('}'); return sb.toString(); } } /** * A fully synchronized KeyedPooledObjectFactory that wraps a * KeyedPooledObjectFactory and synchronizes access to the wrapped factory * methods. *

* Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. *

* * @param pooled object factory key type * @param pooled object factory key value */ private static final class SynchronizedKeyedPooledObjectFactory implements KeyedPooledObjectFactory { /** Synchronization lock */ private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); /** Wrapped factory */ private final KeyedPooledObjectFactory keyedFactory; /** * Creates a SynchronizedKeyedPoolableObjectFactory wrapping the given * factory. * * @param keyedFactory * underlying factory to wrap * @throws IllegalArgumentException * if the factory is null */ SynchronizedKeyedPooledObjectFactory( final KeyedPooledObjectFactory keyedFactory) throws IllegalArgumentException { if (keyedFactory == null) { throw new IllegalArgumentException( "keyedFactory must not be null."); } this.keyedFactory = keyedFactory; } /** * {@inheritDoc} */ @Override public void activateObject(final K key, final PooledObject p) throws Exception { writeLock.lock(); try { keyedFactory.activateObject(key, p); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void destroyObject(final K key, final PooledObject p) throws Exception { writeLock.lock(); try { keyedFactory.destroyObject(key, p); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public PooledObject makeObject(final K key) throws Exception { writeLock.lock(); try { return keyedFactory.makeObject(key); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void passivateObject(final K key, final PooledObject p) throws Exception { writeLock.lock(); try { keyedFactory.passivateObject(key, p); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("SynchronizedKeyedPoolableObjectFactory"); sb.append("{keyedFactory=").append(keyedFactory); sb.append('}'); return sb.toString(); } /** * {@inheritDoc} */ @Override public boolean validateObject(final K key, final PooledObject p) { writeLock.lock(); try { return keyedFactory.validateObject(key, p); } finally { writeLock.unlock(); } } } /** * A synchronized (thread-safe) ObjectPool backed by the specified * ObjectPool. *

* Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. Wrapping a pool that {@link #wait() waits} for poolable * objects to be returned before allowing another one to be borrowed with * another layer of synchronization will cause liveliness issues or a * deadlock. *

* * @param type of objects in the pool */ private static final class SynchronizedObjectPool implements ObjectPool { /** * Object whose monitor is used to synchronize methods on the wrapped * pool. */ private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); /** the underlying object pool */ private final ObjectPool pool; /** * Creates a new SynchronizedObjectPool wrapping the given pool. * * @param pool * the ObjectPool to be "wrapped" in a synchronized * ObjectPool. * @throws IllegalArgumentException * if the pool is null */ SynchronizedObjectPool(final ObjectPool pool) throws IllegalArgumentException { if (pool == null) { throw new IllegalArgumentException(MSG_NULL_POOL); } this.pool = pool; } /** * {@inheritDoc} */ @Override public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { pool.addObject(); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public T borrowObject() throws Exception, NoSuchElementException, IllegalStateException { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { return pool.borrowObject(); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void clear() throws Exception, UnsupportedOperationException { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { pool.clear(); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void close() { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { pool.close(); } catch (final Exception e) { // swallowed as of Pool 2 } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public int getNumActive() { final ReadLock readLock = readWriteLock.readLock(); readLock.lock(); try { return pool.getNumActive(); } finally { readLock.unlock(); } } /** * {@inheritDoc} */ @Override public int getNumIdle() { final ReadLock readLock = readWriteLock.readLock(); readLock.lock(); try { return pool.getNumIdle(); } finally { readLock.unlock(); } } /** * {@inheritDoc} */ @Override public void invalidateObject(final T obj) { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { pool.invalidateObject(obj); } catch (final Exception e) { // swallowed as of Pool 2 } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void returnObject(final T obj) { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { pool.returnObject(obj); } catch (final Exception e) { // swallowed as of Pool 2 } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("SynchronizedObjectPool"); sb.append("{pool=").append(pool); sb.append('}'); return sb.toString(); } } /** * A fully synchronized PooledObjectFactory that wraps a * PooledObjectFactory and synchronizes access to the wrapped factory * methods. *

* Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. *

* * @param pooled object factory type */ private static final class SynchronizedPooledObjectFactory implements PooledObjectFactory { /** Synchronization lock */ private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); /** Wrapped factory */ private final PooledObjectFactory factory; /** * Creates a SynchronizedPoolableObjectFactory wrapping the given * factory. * * @param factory * underlying factory to wrap * @throws IllegalArgumentException * if the factory is null */ SynchronizedPooledObjectFactory(final PooledObjectFactory factory) throws IllegalArgumentException { if (factory == null) { throw new IllegalArgumentException("factory must not be null."); } this.factory = factory; } /** * {@inheritDoc} */ @Override public void activateObject(final PooledObject p) throws Exception { writeLock.lock(); try { factory.activateObject(p); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void destroyObject(final PooledObject p) throws Exception { writeLock.lock(); try { factory.destroyObject(p); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public PooledObject makeObject() throws Exception { writeLock.lock(); try { return factory.makeObject(); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void passivateObject(final PooledObject p) throws Exception { writeLock.lock(); try { factory.passivateObject(p); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("SynchronizedPoolableObjectFactory"); sb.append("{factory=").append(factory); sb.append('}'); return sb.toString(); } /** * {@inheritDoc} */ @Override public boolean validateObject(final PooledObject p) { writeLock.lock(); try { return factory.validateObject(p); } finally { writeLock.unlock(); } } } /** * Timer used to periodically check pools idle object count. Because a * {@link Timer} creates a {@link Thread}, an IODH is used. */ static class TimerHolder { static final Timer MIN_IDLE_TIMER = new Timer(true); } private static final String MSG_FACTOR_NEGATIVE = "factor must be positive."; private static final String MSG_MIN_IDLE = "minIdle must be non-negative."; static final String MSG_NULL_KEY = "key must not be null."; private static final String MSG_NULL_KEYED_POOL = "keyedPool must not be null."; static final String MSG_NULL_KEYS = "keys must not be null."; private static final String MSG_NULL_POOL = "pool must not be null."; /** * Periodically check the idle object count for each key in the * {@code Collection keys} in the keyedPool. At most one idle object will be * added per period. * * @param keyedPool * the keyedPool to check periodically. * @param keys * a collection of keys to check the idle object count. * @param minIdle * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than * this then add an idle object. * @param period * the frequency to check the number of idle objects in a * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. * @param the type of the pool key * @param the type of pool entries * @return a {@link Map} of key and {@link TimerTask} pairs that will * periodically check the pools idle object count. * @throws IllegalArgumentException * when {@code keyedPool}, {@code keys}, or any of the values in * the collection is {@code null} or when {@code minIdle} is * negative or when {@code period} isn't valid for * {@link Timer#schedule(TimerTask, long, long)}. * @see #checkMinIdle(KeyedObjectPool, Object, int, long) */ public static Map checkMinIdle( final KeyedObjectPool keyedPool, final Collection keys, final int minIdle, final long period) throws IllegalArgumentException { if (keys == null) { throw new IllegalArgumentException(MSG_NULL_KEYS); } final Map tasks = new HashMap<>(keys.size()); final Iterator iter = keys.iterator(); while (iter.hasNext()) { final K key = iter.next(); final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period); tasks.put(key, task); } return tasks; } /** * Periodically check the idle object count for the key in the keyedPool. At * most one idle object will be added per period. If there is an exception * when calling {@link KeyedObjectPool#addObject(Object)} then no more * checks for that key will be performed. * * @param keyedPool * the keyedPool to check periodically. * @param key * the key to check the idle count of. * @param minIdle * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than * this then add an idle object. * @param period * the frequency to check the number of idle objects in a * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. * @param the type of the pool key * @param the type of pool entries * @return the {@link TimerTask} that will periodically check the pools idle * object count. * @throws IllegalArgumentException * when {@code keyedPool}, {@code key} is {@code null} or * when {@code minIdle} is negative or when {@code period} isn't * valid for {@link Timer#schedule(TimerTask, long, long)}. */ public static TimerTask checkMinIdle( final KeyedObjectPool keyedPool, final K key, final int minIdle, final long period) throws IllegalArgumentException { if (keyedPool == null) { throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); } if (key == null) { throw new IllegalArgumentException(MSG_NULL_KEY); } if (minIdle < 0) { throw new IllegalArgumentException(MSG_MIN_IDLE); } final TimerTask task = new KeyedObjectPoolMinIdleTimerTask<>( keyedPool, key, minIdle); getMinIdleTimer().schedule(task, 0L, period); return task; } /** * Periodically check the idle object count for the pool. At most one idle * object will be added per period. If there is an exception when calling * {@link ObjectPool#addObject()} then no more checks will be performed. * * @param pool * the pool to check periodically. * @param minIdle * if the {@link ObjectPool#getNumIdle()} is less than this then * add an idle object. * @param period * the frequency to check the number of idle objects in a pool, * see {@link Timer#schedule(TimerTask, long, long)}. * @param the type of objects in the pool * @return the {@link TimerTask} that will periodically check the pools idle * object count. * @throws IllegalArgumentException * when {@code pool} is {@code null} or when {@code minIdle} is * negative or when {@code period} isn't valid for * {@link Timer#schedule(TimerTask, long, long)} */ public static TimerTask checkMinIdle(final ObjectPool pool, final int minIdle, final long period) throws IllegalArgumentException { if (pool == null) { throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); } if (minIdle < 0) { throw new IllegalArgumentException(MSG_MIN_IDLE); } final TimerTask task = new ObjectPoolMinIdleTimerTask<>(pool, minIdle); getMinIdleTimer().schedule(task, 0L, period); return task; } /** * Should the supplied Throwable be re-thrown (eg if it is an instance of * one of the Throwables that should never be swallowed). Used by the pool * error handling for operations that throw exceptions that normally need to * be ignored. * * @param t * The Throwable to check * @throws ThreadDeath * if that is passed in * @throws VirtualMachineError * if that is passed in */ public static void checkRethrow(final Throwable t) { if (t instanceof ThreadDeath) { throw (ThreadDeath) t; } if (t instanceof VirtualMachineError) { throw (VirtualMachineError) t; } // All other instances of Throwable will be silently swallowed } /** * Returns a pool that adaptively decreases its size when idle objects are * no longer needed. This is intended as an always thread-safe alternative * to using an idle object evictor provided by many pool implementations. * This is also an effective way to shrink FIFO ordered pools that * experience load spikes. * * @param keyedPool * the KeyedObjectPool to be decorated so it shrinks its idle * count when possible. * @param the type of the pool key * @param the type of pool entries * @throws IllegalArgumentException * when {@code keyedPool} is {@code null}. * @return a pool that adaptively decreases its size when idle objects are * no longer needed. * @see #erodingPool(KeyedObjectPool, float) * @see #erodingPool(KeyedObjectPool, float, boolean) */ public static KeyedObjectPool erodingPool( final KeyedObjectPool keyedPool) { return erodingPool(keyedPool, 1f); } /** * Returns a pool that adaptively decreases its size when idle objects are * no longer needed. This is intended as an always thread-safe alternative * to using an idle object evictor provided by many pool implementations. * This is also an effective way to shrink FIFO ordered pools that * experience load spikes. *

* The factor parameter provides a mechanism to tweak the rate at which the * pool tries to shrink its size. Values between 0 and 1 cause the pool to * try to shrink its size more often. Values greater than 1 cause the pool * to less frequently try to shrink its size. *

* * @param keyedPool * the KeyedObjectPool to be decorated so it shrinks its idle * count when possible. * @param factor * a positive value to scale the rate at which the pool tries to * reduce its size. If 0 < factor < 1 then the pool * shrinks more aggressively. If 1 < factor then the pool * shrinks less aggressively. * @param the type of the pool key * @param the type of pool entries * @throws IllegalArgumentException * when {@code keyedPool} is {@code null} or when {@code factor} * is not positive. * @return a pool that adaptively decreases its size when idle objects are * no longer needed. * @see #erodingPool(KeyedObjectPool, float, boolean) */ public static KeyedObjectPool erodingPool( final KeyedObjectPool keyedPool, final float factor) { return erodingPool(keyedPool, factor, false); } /** * Returns a pool that adaptively decreases its size when idle objects are * no longer needed. This is intended as an always thread-safe alternative * to using an idle object evictor provided by many pool implementations. * This is also an effective way to shrink FIFO ordered pools that * experience load spikes. *

* The factor parameter provides a mechanism to tweak the rate at which the * pool tries to shrink its size. Values between 0 and 1 cause the pool to * try to shrink its size more often. Values greater than 1 cause the pool * to less frequently try to shrink its size. *

*

* The perKey parameter determines if the pool shrinks on a whole pool basis * or a per key basis. When perKey is false, the keys do not have an effect * on the rate at which the pool tries to shrink its size. When perKey is * true, each key is shrunk independently. *

* * @param keyedPool * the KeyedObjectPool to be decorated so it shrinks its idle * count when possible. * @param factor * a positive value to scale the rate at which the pool tries to * reduce its size. If 0 < factor < 1 then the pool * shrinks more aggressively. If 1 < factor then the pool * shrinks less aggressively. * @param perKey * when true, each key is treated independently. * @param the type of the pool key * @param the type of pool entries * @throws IllegalArgumentException * when {@code keyedPool} is {@code null} or when {@code factor} * is not positive. * @return a pool that adaptively decreases its size when idle objects are * no longer needed. * @see #erodingPool(KeyedObjectPool) * @see #erodingPool(KeyedObjectPool, float) */ public static KeyedObjectPool erodingPool( final KeyedObjectPool keyedPool, final float factor, final boolean perKey) { if (keyedPool == null) { throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); } if (factor <= 0f) { throw new IllegalArgumentException(MSG_FACTOR_NEGATIVE); } if (perKey) { return new ErodingPerKeyKeyedObjectPool<>(keyedPool, factor); } return new ErodingKeyedObjectPool<>(keyedPool, factor); } /** * Returns a pool that adaptively decreases its size when idle objects are * no longer needed. This is intended as an always thread-safe alternative * to using an idle object evictor provided by many pool implementations. * This is also an effective way to shrink FIFO ordered pools that * experience load spikes. * * @param pool * the ObjectPool to be decorated so it shrinks its idle count * when possible. * @param the type of objects in the pool * @throws IllegalArgumentException * when {@code pool} is {@code null}. * @return a pool that adaptively decreases its size when idle objects are * no longer needed. * @see #erodingPool(ObjectPool, float) */ public static ObjectPool erodingPool(final ObjectPool pool) { return erodingPool(pool, 1f); } /** * Returns a pool that adaptively decreases its size when idle objects are * no longer needed. This is intended as an always thread-safe alternative * to using an idle object evictor provided by many pool implementations. * This is also an effective way to shrink FIFO ordered pools that * experience load spikes. *

* The factor parameter provides a mechanism to tweak the rate at which the * pool tries to shrink its size. Values between 0 and 1 cause the pool to * try to shrink its size more often. Values greater than 1 cause the pool * to less frequently try to shrink its size. *

* * @param pool * the ObjectPool to be decorated so it shrinks its idle count * when possible. * @param factor * a positive value to scale the rate at which the pool tries to * reduce its size. If 0 < factor < 1 then the pool * shrinks more aggressively. If 1 < factor then the pool * shrinks less aggressively. * @param the type of objects in the pool * @throws IllegalArgumentException * when {@code pool} is {@code null} or when {@code factor} is * not positive. * @return a pool that adaptively decreases its size when idle objects are * no longer needed. * @see #erodingPool(ObjectPool) */ public static ObjectPool erodingPool(final ObjectPool pool, final float factor) { if (pool == null) { throw new IllegalArgumentException(MSG_NULL_POOL); } if (factor <= 0f) { throw new IllegalArgumentException(MSG_FACTOR_NEGATIVE); } return new ErodingObjectPool<>(pool, factor); } /** * Gets the {@code Timer} for checking keyedPool's idle count. * * @return the {@link Timer} for checking keyedPool's idle count. */ private static Timer getMinIdleTimer() { return TimerHolder.MIN_IDLE_TIMER; } /** * Calls {@link KeyedObjectPool#addObject(Object)} on {@code keyedPool} with * each key in {@code keys} for {@code count} number of times. This has * the same effect as calling {@link #prefill(KeyedObjectPool, Object, int)} * for each key in the {@code keys} collection. * * @param keyedPool * the keyedPool to prefill. * @param keys * {@link Collection} of keys to add objects for. * @param count * the number of idle objects to add for each {@code key}. * @param the type of the pool key * @param the type of pool entries * @throws Exception * when {@link KeyedObjectPool#addObject(Object)} fails. * @throws IllegalArgumentException * when {@code keyedPool}, {@code keys}, or any value in * {@code keys} is {@code null}. * @see #prefill(KeyedObjectPool, Object, int) * @deprecated Use {@link KeyedObjectPool#addObjects(Collection, int)}. */ @Deprecated public static void prefill(final KeyedObjectPool keyedPool, final Collection keys, final int count) throws Exception, IllegalArgumentException { if (keys == null) { throw new IllegalArgumentException(MSG_NULL_KEYS); } keyedPool.addObjects(keys, count); } /** * Calls {@link KeyedObjectPool#addObject(Object)} on {@code keyedPool} with * {@code key} {@code count} number of times. * * @param keyedPool * the keyedPool to prefill. * @param key * the key to add objects for. * @param count * the number of idle objects to add for {@code key}. * @param the type of the pool key * @param the type of pool entries * @throws Exception * when {@link KeyedObjectPool#addObject(Object)} fails. * @throws IllegalArgumentException * when {@code keyedPool} or {@code key} is {@code null}. * @deprecated Use {@link KeyedObjectPool#addObjects(Object, int)}. */ @Deprecated public static void prefill(final KeyedObjectPool keyedPool, final K key, final int count) throws Exception, IllegalArgumentException { if (keyedPool == null) { throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); } keyedPool.addObjects(key, count); } /** * Calls {@link ObjectPool#addObject()} on {@code pool} {@code count} number * of times. * * @param pool * the pool to prefill. * @param count * the number of idle objects to add. * @param the type of objects in the pool * @throws Exception * when {@link ObjectPool#addObject()} fails. * @throws IllegalArgumentException * when {@code pool} is {@code null}. * @deprecated Use {@link ObjectPool#addObjects(int)}. */ @Deprecated public static void prefill(final ObjectPool pool, final int count) throws Exception, IllegalArgumentException { if (pool == null) { throw new IllegalArgumentException(MSG_NULL_POOL); } pool.addObjects(count); } /** * Returns a synchronized (thread-safe) KeyedPooledObjectFactory backed by * the specified KeyedPoolableObjectFactory. * * @param keyedFactory * the KeyedPooledObjectFactory to be "wrapped" in a * synchronized KeyedPooledObjectFactory. * @param the type of the pool key * @param the type of pool entries * @return a synchronized view of the specified KeyedPooledObjectFactory. */ public static KeyedPooledObjectFactory synchronizedKeyedPooledFactory( final KeyedPooledObjectFactory keyedFactory) { return new SynchronizedKeyedPooledObjectFactory<>(keyedFactory); } /** * Returns a synchronized (thread-safe) KeyedObjectPool backed by the * specified KeyedObjectPool. *

* Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. Wrapping a pool that {@link #wait() waits} for poolable * objects to be returned before allowing another one to be borrowed with * another layer of synchronization will cause liveliness issues or a * deadlock. *

* * @param keyedPool * the KeyedObjectPool to be "wrapped" in a synchronized * KeyedObjectPool. * @param the type of the pool key * @param the type of pool entries * @return a synchronized view of the specified KeyedObjectPool. */ public static KeyedObjectPool synchronizedPool( final KeyedObjectPool keyedPool) { /* * assert !(keyedPool instanceof GenericKeyedObjectPool) : * "GenericKeyedObjectPool is already thread-safe"; assert !(keyedPool * instanceof StackKeyedObjectPool) : * "StackKeyedObjectPool is already thread-safe"; assert * !"org.apache.commons.pool.composite.CompositeKeyedObjectPool" * .equals(keyedPool.getClass().getName()) : * "CompositeKeyedObjectPools are already thread-safe"; */ return new SynchronizedKeyedObjectPool<>(keyedPool); } /** * Returns a synchronized (thread-safe) ObjectPool backed by the specified * ObjectPool. *

* Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. Wrapping a pool that {@link #wait() waits} for poolable * objects to be returned before allowing another one to be borrowed with * another layer of synchronization will cause liveliness issues or a * deadlock. *

* * @param pool * the ObjectPool to be "wrapped" in a synchronized ObjectPool. * @param the type of objects in the pool * @throws IllegalArgumentException * when {@code pool} is {@code null}. * @return a synchronized view of the specified ObjectPool. */ public static ObjectPool synchronizedPool(final ObjectPool pool) { if (pool == null) { throw new IllegalArgumentException(MSG_NULL_POOL); } /* * assert !(pool instanceof GenericObjectPool) : * "GenericObjectPool is already thread-safe"; assert !(pool instanceof * SoftReferenceObjectPool) : * "SoftReferenceObjectPool is already thread-safe"; assert !(pool * instanceof StackObjectPool) : * "StackObjectPool is already thread-safe"; assert * !"org.apache.commons.pool.composite.CompositeObjectPool" * .equals(pool.getClass().getName()) : * "CompositeObjectPools are already thread-safe"; */ return new SynchronizedObjectPool<>(pool); } /** * Returns a synchronized (thread-safe) PooledObjectFactory backed by the * specified PooledObjectFactory. * * @param factory * the PooledObjectFactory to be "wrapped" in a synchronized * PooledObjectFactory. * @param the type of objects in the pool * @return a synchronized view of the specified PooledObjectFactory. */ public static PooledObjectFactory synchronizedPooledFactory( final PooledObjectFactory factory) { return new SynchronizedPooledObjectFactory<>(factory); } /** * PoolUtils instances should NOT be constructed in standard programming. * Instead, the class should be used procedurally: PoolUtils.adapt(aPool);. * This constructor is public to permit tools that require a JavaBean * instance to operate. */ public PoolUtils() { } } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/PooledObject.java000066400000000000000000000167331405425132200320120ustar00rootroot00000000000000/* * 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.commons.pool2; import java.io.PrintWriter; import java.time.Duration; import java.util.Deque; /** * Defines the wrapper that is used to track the additional information, such as * state, for the pooled objects. *

* Implementations of this class are required to be thread-safe. *

* * @param the type of object in the pool * * @since 2.0 */ public interface PooledObject extends Comparable> { /** * Allocates the object. * * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} */ boolean allocate(); /** * Orders instances based on idle time - i.e. the length of time since the * instance was returned to the pool. Used by the GKOP idle object evictor. *

* Note: This class has a natural ordering that is inconsistent with * equals if distinct objects have the same identity hash code. *

*

* {@inheritDoc} *

*/ @Override int compareTo(PooledObject other); /** * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE} * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}. * * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED} */ boolean deallocate(); /** * Called to inform the object that the eviction test has ended. * * @param idleQueue The queue of idle objects to which the object should be * returned * * @return Currently not used */ boolean endEvictionTest(Deque> idleQueue); @Override boolean equals(Object obj); /** * Gets the amount of time this object last spent in the * active state (it may still be active in which case subsequent calls will * return an increased value). * * @return The duration last spent in the active state * @since 2.12.0 */ default Duration getActiveTime() { return Duration.ofMillis(getActiveTimeMillis()); } /** * Gets the amount of time in milliseconds this object last spent in the * active state (it may still be active in which case subsequent calls will * return an increased value). * * @return The time in milliseconds last spent in the active state */ long getActiveTimeMillis(); /** * Gets the number of times this object has been borrowed. * * @return -1 by default for old implementations prior to release 2.7.0. * @since 2.7.0 */ default long getBorrowedCount() { return -1; } /** * Gets the time (using the same basis as * {@link System#currentTimeMillis()}) that this object was created. * * @return The creation time for the wrapped object */ long getCreateTime(); /** * Gets the amount of time that this object last spend in the * idle state (it may still be idle in which case subsequent calls will * return an increased value). * * @return The amount of time in last spent in the idle state. * @since 2.10.0 */ default Duration getIdleTime() { return Duration.ofMillis(getIdleTimeMillis()); } /** * Gets the amount of time in milliseconds that this object last spend in the * idle state (it may still be idle in which case subsequent calls will * return an increased value). * * @return The time in milliseconds last spent in the idle state */ long getIdleTimeMillis(); /** * Gets the time the wrapped object was last borrowed. * * @return The time the object was last borrowed */ long getLastBorrowTime(); /** * Gets the time the wrapped object was last returned. * * @return The time the object was last returned */ long getLastReturnTime(); /** * Gets an estimate of the last time this object was used. If the class * of the pooled object implements {@link TrackedUse}, what is returned is * the maximum of {@link TrackedUse#getLastUsed()} and * {@link #getLastBorrowTime()}; otherwise this method gives the same * value as {@link #getLastBorrowTime()}. * * @return the last time this object was used */ long getLastUsedTime(); /** * Gets the underlying object that is wrapped by this instance of * {@link PooledObject}. * * @return The wrapped object */ T getObject(); /** * Gets the state of this object. * @return state */ PooledObjectState getState(); @Override int hashCode(); /** * Sets the state to {@link PooledObjectState#INVALID INVALID} */ void invalidate(); /** * Marks the pooled object as abandoned. */ void markAbandoned(); /** * Marks the object as returning to the pool. */ void markReturning(); /** * Prints the stack trace of the code that borrowed this pooled object and * the stack trace of the last code to use this object (if available) to * the supplied writer. * * @param writer The destination for the debug output */ void printStackTrace(PrintWriter writer); /** * Is abandoned object tracking being used? If this is true the * implementation will need to record the stack trace of the last caller to * borrow this object. * * @param logAbandoned The new configuration setting for abandoned * object tracking */ void setLogAbandoned(boolean logAbandoned); /** * Configures the stack trace generation strategy based on whether or not fully detailed stack traces are required. * When set to false, abandoned logs may only include caller class information rather than method names, line * numbers, and other normal metadata available in a full stack trace. * * @param requireFullStackTrace the new configuration setting for abandoned object logging * @since 2.7.0 */ default void setRequireFullStackTrace(final boolean requireFullStackTrace) { // noop } /** * Attempts to place the pooled object in the * {@link PooledObjectState#EVICTION} state. * * @return {@code true} if the object was placed in the * {@link PooledObjectState#EVICTION} state otherwise * {@code false} */ boolean startEvictionTest(); /** * Provides a String form of the wrapper for debug purposes. The format is * not fixed and may change at any time. *

* {@inheritDoc} */ @Override String toString(); /** * Record the current stack trace as the last time the object was used. */ void use(); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java000066400000000000000000000135141405425132200333340ustar00rootroot00000000000000/* * 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.commons.pool2; /** * An interface defining life-cycle methods for instances to be served by an * {@link ObjectPool}. *

* By contract, when an {@link ObjectPool} delegates to a * {@link PooledObjectFactory}, *

*
    *
  1. * {@link #makeObject} is called whenever a new instance is needed. *
  2. *
  3. * {@link #activateObject} is invoked on every instance that has been * {@link #passivateObject passivated} before it is * {@link ObjectPool#borrowObject borrowed} from the pool. *
  4. *
  5. * {@link #validateObject} may be invoked on {@link #activateObject activated} * instances to make sure they can be {@link ObjectPool#borrowObject borrowed} * from the pool. {@link #validateObject} may also be used to * test an instance being {@link ObjectPool#returnObject returned} to the pool * before it is {@link #passivateObject passivated}. It will only be invoked * on an activated instance. *
  6. *
  7. * {@link #passivateObject} is invoked on every instance when it is returned * to the pool. *
  8. *
  9. * {@link #destroyObject} is invoked on every instance when it is being * "dropped" from the pool (whether due to the response from * {@link #validateObject}, or for reasons specific to the pool * implementation.) There is no guarantee that the instance being destroyed * will be considered active, passive or in a generally consistent state. *
  10. *
* {@link PooledObjectFactory} must be thread-safe. The only promise * an {@link ObjectPool} makes is that the same instance of an object will not * be passed to more than one method of a {@code PoolableObjectFactory} * at a time. *

* While clients of a {@link KeyedObjectPool} borrow and return instances of * the underlying value type {@code V}, the factory methods act on instances of * {@link PooledObject PooledObject<V>}. These are the object wrappers that * pools use to track and maintain state information about the objects that * they manage. *

* * @param Type of element managed in this factory. * * @see ObjectPool * * @since 2.0 */ public interface PooledObjectFactory { /** * Reinitializes an instance to be returned by the pool. * * @param p a {@code PooledObject} wrapping the instance to be activated * * @throws Exception if there is a problem activating {@code obj}, * this exception may be swallowed by the pool. * * @see #destroyObject */ void activateObject(PooledObject p) throws Exception; /** * Destroys an instance no longer needed by the pool, using the default (NORMAL) * DestroyMode. *

* It is important for implementations of this method to be aware that there * is no guarantee about what state {@code obj} will be in and the * implementation should be prepared to handle unexpected errors. *

*

* Also, an implementation must take in to consideration that instances lost * to the garbage collector may never be destroyed. *

* * @param p a {@code PooledObject} wrapping the instance to be destroyed * * @throws Exception should be avoided as it may be swallowed by * the pool implementation. * * @see #validateObject * @see ObjectPool#invalidateObject */ void destroyObject(PooledObject p) throws Exception; /** * Destroys an instance no longer needed by the pool, using the provided * DestroyMode. * * @param p a {@code PooledObject} wrapping the instance to be destroyed * @param mode DestroyMode providing context to the factory * * @throws Exception should be avoided as it may be swallowed by * the pool implementation. * * @see #validateObject * @see ObjectPool#invalidateObject * @see #destroyObject(PooledObject) * @see DestroyMode * @since 2.9.0 */ default void destroyObject(final PooledObject p, final DestroyMode mode) throws Exception { destroyObject(p); } /** * Creates an instance that can be served by the pool and wrap it in a * {@link PooledObject} to be managed by the pool. * * @return a {@code PooledObject} wrapping an instance that can be served by the pool * * @throws Exception if there is a problem creating a new instance, * this will be propagated to the code requesting an object. */ PooledObject makeObject() throws Exception; /** * Uninitializes an instance to be returned to the idle object pool. * * @param p a {@code PooledObject} wrapping the instance to be passivated * * @throws Exception if there is a problem passivating {@code obj}, * this exception may be swallowed by the pool. * * @see #destroyObject */ void passivateObject(PooledObject p) throws Exception; /** * Ensures that the instance is safe to be returned by the pool. * * @param p a {@code PooledObject} wrapping the instance to be validated * * @return {@code false} if {@code obj} is not valid and should * be dropped from the pool, {@code true} otherwise. */ boolean validateObject(PooledObject p); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/PooledObjectState.java000066400000000000000000000047531405425132200330120ustar00rootroot00000000000000/* * 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.commons.pool2; /** * Provides the possible states that a {@link PooledObject} may be in. * * @since 2.0 */ public enum PooledObjectState { /** * In the queue, not in use. */ IDLE, /** * In use. */ ALLOCATED, /** * In the queue, currently being tested for possible eviction. */ EVICTION, /** * Not in the queue, currently being tested for possible eviction. An * attempt to borrow the object was made while being tested which removed it * from the queue. It should be returned to the head of the queue once * eviction testing completes. * TODO: Consider allocating object and ignoring the result of the eviction * test. */ EVICTION_RETURN_TO_HEAD, /** * In the queue, currently being validated. */ VALIDATION, /** * Not in queue, currently being validated. The object was borrowed while * being validated and since testOnBorrow was configured, it was removed * from the queue and pre-allocated. It should be allocated once validation * completes. */ VALIDATION_PREALLOCATED, /** * Not in queue, currently being validated. An attempt to borrow the object * was made while previously being tested for eviction which removed it from * the queue. It should be returned to the head of the queue once validation * completes. */ VALIDATION_RETURN_TO_HEAD, /** * Failed maintenance (e.g. eviction test or validation) and will be / has * been destroyed */ INVALID, /** * Deemed abandoned, to be invalidated. */ ABANDONED, /** * Returning to the pool. */ RETURNING } SwallowedExceptionListener.java000066400000000000000000000026241405425132200347020ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/* * 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.commons.pool2; /** * Pools that unavoidably swallow exceptions may be configured with an instance * of this listener so the user may receive notification of when this happens. * The listener should not throw an exception when called but pools calling * listeners should protect themselves against exceptions anyway. * * @since 2.0 */ public interface SwallowedExceptionListener { /** * This method is called every time the implementation unavoidably swallows * an exception. * * @param e The exception that was swallowed */ void onSwallowException(Exception e); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/TrackedUse.java000066400000000000000000000025421405425132200314640ustar00rootroot00000000000000/* * 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.commons.pool2; /** * This interface allows pooled objects to make information available about when * and how they were used available to the object pool. The object pool may, but * is not required, to use this information to make more informed decisions when * determining the state of a pooled object - for instance whether or not the * object has been abandoned. * * @since 2.0 */ public interface TrackedUse { /** * Get the last time this object was used in ms. * * @return long time in ms */ long getLastUsed(); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/UsageTracking.java000066400000000000000000000027621405425132200321650ustar00rootroot00000000000000/* * 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.commons.pool2; /** * This interface may be implemented by an object pool to enable clients * (primarily those clients that wrap pools to provide pools with extended * features) to provide additional information to the pool relating to object * using allowing more informed decisions and reporting to be made regarding * abandoned objects. * * @param The type of object provided by the pool. * * @since 2.0 */ public interface UsageTracking { /** * This method is called every time a pooled object is used to enable the pool to * better track borrowed objects. * * @param pooledObject The object that is being used */ void use(T pooledObject); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/000077500000000000000000000000001405425132200275255ustar00rootroot00000000000000AbandonedConfig.java000066400000000000000000000271401405425132200333160ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.Charset; import java.time.Duration; import org.apache.commons.pool2.TrackedUse; import org.apache.commons.pool2.UsageTracking; /** * Configuration settings for abandoned object removal. * * @since 2.0 */ public class AbandonedConfig { private static final Duration DEFAULT_REMOVE_ABANDONED_TIMEOUT = Duration.ofSeconds(300); /** * Whether or not borrowObject performs abandoned object removal. */ private boolean removeAbandonedOnBorrow; /** * Whether or not pool maintenance (evictor) performs abandoned object * removal. */ private boolean removeAbandonedOnMaintenance; /** * Timeout before an abandoned object can be removed. */ private Duration removeAbandonedTimeout = DEFAULT_REMOVE_ABANDONED_TIMEOUT; /** * Determines whether or not to log stack traces for application code * which abandoned an object. */ private boolean logAbandoned = false; /** * Determines whether or not to log full stack traces when logAbandoned is true. * If disabled, then a faster method for logging stack traces with only class data * may be used if possible. * * @since 2.5 */ private boolean requireFullStackTrace = true; /** * PrintWriter to use to log information on abandoned objects. * Use of default system encoding is deliberate. */ private PrintWriter logWriter = new PrintWriter(new OutputStreamWriter(System.out, Charset.defaultCharset())); /** * If the pool implements {@link UsageTracking}, should the pool record a * stack trace every time a method is called on a pooled object and retain * the most recent stack trace to aid debugging of abandoned objects? */ private boolean useUsageTracking = false; /** * Flag to log stack traces for application code which abandoned * an object. * * Defaults to false. * Logging of abandoned objects adds overhead for every object created * because a stack trace has to be generated. * * @return boolean true if stack trace logging is turned on for abandoned * objects * */ public boolean getLogAbandoned() { return this.logAbandoned; } /** * Gets the log writer being used by this configuration to log * information on abandoned objects. If not set, a PrintWriter based on * System.out with the system default encoding is used. * * @return log writer in use */ public PrintWriter getLogWriter() { return logWriter; } /** *

Flag to remove abandoned objects if they exceed the * removeAbandonedTimeout when borrowObject is invoked.

* *

The default value is false.

* *

If set to true, abandoned objects are removed by borrowObject if * there are fewer than 2 idle objects available in the pool and * {@code getNumActive() > getMaxTotal() - 3}

* * @return true if abandoned objects are to be removed by borrowObject */ public boolean getRemoveAbandonedOnBorrow() { return this.removeAbandonedOnBorrow; } /** *

Flag to remove abandoned objects if they exceed the * removeAbandonedTimeout when pool maintenance (the "evictor") * runs.

* *

The default value is false.

* *

If set to true, abandoned objects are removed by the pool * maintenance thread when it runs. This setting has no effect * unless maintenance is enabled by setting *{@link GenericObjectPool#getTimeBetweenEvictionRuns() timeBetweenEvictionRuns} * to a positive number.

* * @return true if abandoned objects are to be removed by the evictor */ public boolean getRemoveAbandonedOnMaintenance() { return this.removeAbandonedOnMaintenance; } /** *

Timeout in seconds before an abandoned object can be removed.

* *

The time of most recent use of an object is the maximum (latest) of * {@link TrackedUse#getLastUsed()} (if this class of the object implements * TrackedUse) and the time when the object was borrowed from the pool.

* *

The default value is 300 seconds.

* * @return the abandoned object timeout in seconds * @deprecated Use {@link #getRemoveAbandonedTimeoutDuration()}. */ @Deprecated public int getRemoveAbandonedTimeout() { return (int) this.removeAbandonedTimeout.getSeconds(); } /** *

Timeout before an abandoned object can be removed.

* *

The time of most recent use of an object is the maximum (latest) of * {@link TrackedUse#getLastUsed()} (if this class of the object implements * TrackedUse) and the time when the object was borrowed from the pool.

* *

The default value is 300 seconds.

* * @return the abandoned object timeout. * @since 2.10.0 */ public Duration getRemoveAbandonedTimeoutDuration() { return this.removeAbandonedTimeout; } /** * Indicates if full stack traces are required when {@link #getLogAbandoned() logAbandoned} * is true. Defaults to true. Logging of abandoned objects requiring a full stack trace will * generate an entire stack trace to generate for every object created. If this is disabled, * a faster but less informative stack walking mechanism may be used if available. * * @return true if full stack traces are required for logging abandoned connections, or false * if abbreviated stack traces are acceptable * @see CallStack * @since 2.5 */ public boolean getRequireFullStackTrace() { return requireFullStackTrace; } /** * If the pool implements {@link UsageTracking}, should the pool record a * stack trace every time a method is called on a pooled object and retain * the most recent stack trace to aid debugging of abandoned objects? * * @return {@code true} if usage tracking is enabled */ public boolean getUseUsageTracking() { return useUsageTracking; } /** * Sets the flag to log stack traces for application code which abandoned * an object. * * @param logAbandoned true turns on abandoned stack trace logging * @see #getLogAbandoned() * */ public void setLogAbandoned(final boolean logAbandoned) { this.logAbandoned = logAbandoned; } /** * Sets the log writer to be used by this configuration to log * information on abandoned objects. * * @param logWriter The new log writer */ public void setLogWriter(final PrintWriter logWriter) { this.logWriter = logWriter; } /** *

Flag to remove abandoned objects if they exceed the * removeAbandonedTimeout when borrowObject is invoked.

* * @param removeAbandonedOnBorrow true means abandoned objects will be * removed by borrowObject * @see #getRemoveAbandonedOnBorrow() */ public void setRemoveAbandonedOnBorrow(final boolean removeAbandonedOnBorrow) { this.removeAbandonedOnBorrow = removeAbandonedOnBorrow; } /** *

Flag to remove abandoned objects if they exceed the * removeAbandonedTimeout when pool maintenance runs.

* * @param removeAbandonedOnMaintenance true means abandoned objects will be * removed by pool maintenance * @see #getRemoveAbandonedOnMaintenance */ public void setRemoveAbandonedOnMaintenance(final boolean removeAbandonedOnMaintenance) { this.removeAbandonedOnMaintenance = removeAbandonedOnMaintenance; } /** *

Sets the timeout before an abandoned object can be * removed

* *

Setting this property has no effect if * {@link #getRemoveAbandonedOnBorrow() removeAbandonedOnBorrow} and * {@link #getRemoveAbandonedOnMaintenance() removeAbandonedOnMaintenance} * are both false.

* * @param removeAbandonedTimeout new abandoned timeout * @see #getRemoveAbandonedTimeout() * @since 2.10.0 */ public void setRemoveAbandonedTimeout(final Duration removeAbandonedTimeout) { this.removeAbandonedTimeout = removeAbandonedTimeout; } /** *

Sets the timeout in seconds before an abandoned object can be * removed

* *

Setting this property has no effect if * {@link #getRemoveAbandonedOnBorrow() removeAbandonedOnBorrow} and * {@link #getRemoveAbandonedOnMaintenance() removeAbandonedOnMaintenance} * are both false.

* * @param removeAbandonedTimeout new abandoned timeout in seconds * @see #getRemoveAbandonedTimeout() * @deprecated Use {@link #setRemoveAbandonedTimeout(Duration)}. */ @Deprecated public void setRemoveAbandonedTimeout(final int removeAbandonedTimeout) { setRemoveAbandonedTimeout(Duration.ofSeconds(removeAbandonedTimeout)); } /** * Sets the flag to require full stack traces for logging abandoned connections when enabled. * * @param requireFullStackTrace indicates whether or not full stack traces are required in * abandoned connection logs * @see CallStack * @see #getRequireFullStackTrace() * @since 2.5 */ public void setRequireFullStackTrace(final boolean requireFullStackTrace) { this.requireFullStackTrace = requireFullStackTrace; } /** * If the pool implements {@link UsageTracking}, configure whether the pool * should record a stack trace every time a method is called on a pooled * object and retain the most recent stack trace to aid debugging of * abandoned objects. * * @param useUsageTracking A value of {@code true} will enable * the recording of a stack trace on every use * of a pooled object */ public void setUseUsageTracking(final boolean useUsageTracking) { this.useUsageTracking = useUsageTracking; } /** * @since 2.4.3 */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("AbandonedConfig [removeAbandonedOnBorrow="); builder.append(removeAbandonedOnBorrow); builder.append(", removeAbandonedOnMaintenance="); builder.append(removeAbandonedOnMaintenance); builder.append(", removeAbandonedTimeout="); builder.append(removeAbandonedTimeout); builder.append(", logAbandoned="); builder.append(logAbandoned); builder.append(", logWriter="); builder.append(logWriter); builder.append(", useUsageTracking="); builder.append(useUsageTracking); builder.append("]"); return builder.toString(); } } BaseGenericObjectPool.java000066400000000000000000001723311405425132200344500ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.lang.management.ManagementFactory; import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; import java.time.Duration; import java.util.Arrays; import java.util.Deque; import java.util.Iterator; import java.util.TimerTask; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicLong; import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import org.apache.commons.pool2.BaseObject; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectState; import org.apache.commons.pool2.SwallowedExceptionListener; /** * Base class that provides common functionality for {@link GenericObjectPool} * and {@link GenericKeyedObjectPool}. The primary reason this class exists is * reduce code duplication between the two pool implementations. * * @param Type of element pooled in this pool. * * This class is intended to be thread-safe. * * @since 2.0 */ public abstract class BaseGenericObjectPool extends BaseObject { /** * The idle object eviction iterator. Holds a reference to the idle objects. */ class EvictionIterator implements Iterator> { private final Deque> idleObjects; private final Iterator> idleObjectIterator; /** * Create an EvictionIterator for the provided idle instance deque. * @param idleObjects underlying deque */ EvictionIterator(final Deque> idleObjects) { this.idleObjects = idleObjects; if (getLifo()) { idleObjectIterator = idleObjects.descendingIterator(); } else { idleObjectIterator = idleObjects.iterator(); } } /** * Gets the idle object deque referenced by this iterator. * @return the idle object deque */ public Deque> getIdleObjects() { return idleObjects; } /** {@inheritDoc} */ @Override public boolean hasNext() { return idleObjectIterator.hasNext(); } /** {@inheritDoc} */ @Override public PooledObject next() { return idleObjectIterator.next(); } /** {@inheritDoc} */ @Override public void remove() { idleObjectIterator.remove(); } } /** * The idle object evictor {@link TimerTask}. * * @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis */ class Evictor implements Runnable { private ScheduledFuture scheduledFuture; /** * Cancels the scheduled future. */ void cancel() { scheduledFuture.cancel(false); } /** * Run pool maintenance. Evict objects qualifying for eviction and then * ensure that the minimum number of idle instances are available. * Since the Timer that invokes Evictors is shared for all Pools but * pools may exist in different class loaders, the Evictor ensures that * any actions taken are under the class loader of the factory * associated with the pool. */ @Override public void run() { final ClassLoader savedClassLoader = Thread.currentThread().getContextClassLoader(); try { if (factoryClassLoader != null) { // Set the class loader for the factory final ClassLoader cl = factoryClassLoader.get(); if (cl == null) { // The pool has been dereferenced and the class loader // GC'd. Cancel this timer so the pool can be GC'd as // well. cancel(); return; } Thread.currentThread().setContextClassLoader(cl); } // Evict from the pool try { evict(); } catch(final Exception e) { swallowException(e); } catch(final OutOfMemoryError oome) { // Log problem but give evictor thread a chance to continue // in case error is recoverable oome.printStackTrace(System.err); } // Re-create idle instances. try { ensureMinIdle(); } catch (final Exception e) { swallowException(e); } } finally { // Restore the previous CCL Thread.currentThread().setContextClassLoader(savedClassLoader); } } /** * Sets the scheduled future. * * @param scheduledFuture the scheduled future. */ void setScheduledFuture(final ScheduledFuture scheduledFuture) { this.scheduledFuture = scheduledFuture; } } /** * Wrapper for objects under management by the pool. * * GenericObjectPool and GenericKeyedObjectPool maintain references to all * objects under management using maps keyed on the objects. This wrapper * class ensures that objects can work as hash keys. * * @param type of objects in the pool */ static class IdentityWrapper { /** Wrapped object */ private final T instance; /** * Create a wrapper for an instance. * * @param instance object to wrap */ public IdentityWrapper(final T instance) { this.instance = instance; } @Override @SuppressWarnings("rawtypes") public boolean equals(final Object other) { return other instanceof IdentityWrapper && ((IdentityWrapper) other).instance == instance; } /** * @return the wrapped object */ public T getObject() { return instance; } @Override public int hashCode() { return System.identityHashCode(instance); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("IdentityWrapper [instance="); builder.append(instance); builder.append("]"); return builder.toString(); } } /** * Maintains a cache of values for a single metric and reports * statistics on the cached values. */ private class StatsStore { private final AtomicLong[] values; private final int size; private int index; /** * Create a StatsStore with the given cache size. * * @param size number of values to maintain in the cache. */ StatsStore(final int size) { this.size = size; values = new AtomicLong[size]; for (int i = 0; i < size; i++) { values[i] = new AtomicLong(-1); } } void add(final Duration value) { add(value.toMillis()); } /** * Adds a value to the cache. If the cache is full, one of the * existing values is replaced by the new value. * * @param value new value to add to the cache. */ synchronized void add(final long value) { values[index].set(value); index++; if (index == size) { index = 0; } } /** * Gets the mean of the cached values. * * @return the mean of the cache, truncated to long */ public long getMean() { double result = 0; int counter = 0; for (int i = 0; i < size; i++) { final long value = values[i].get(); if (value != -1) { counter++; result = result * ((counter - 1) / (double) counter) + value/(double) counter; } } return (long) result; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("StatsStore [values="); builder.append(Arrays.toString(values)); builder.append(", size="); builder.append(size); builder.append(", index="); builder.append(index); builder.append("]"); return builder.toString(); } } // Constants /** * The size of the caches used to store historical data for some attributes * so that rolling means may be calculated. */ public static final int MEAN_TIMING_STATS_CACHE_SIZE = 100; private static final String EVICTION_POLICY_TYPE_NAME = EvictionPolicy.class.getName(); // Configuration attributes private volatile int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; private volatile boolean blockWhenExhausted = BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED; private volatile Duration maxWait = BaseObjectPoolConfig.DEFAULT_MAX_WAIT; private volatile boolean lifo = BaseObjectPoolConfig.DEFAULT_LIFO; private final boolean fairness; private volatile boolean testOnCreate = BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE; private volatile boolean testOnBorrow = BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW; private volatile boolean testOnReturn = BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN; private volatile boolean testWhileIdle = BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE; private volatile Duration timeBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS; private volatile int numTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN; private volatile Duration minEvictableIdleTime = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME; private volatile Duration softMinEvictableIdleTime = BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME; private volatile EvictionPolicy evictionPolicy; private volatile Duration evictorShutdownTimeout = BaseObjectPoolConfig.DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT; // Internal (primarily state) attributes final Object closeLock = new Object(); volatile boolean closed; final Object evictionLock = new Object(); private Evictor evictor = null; // @GuardedBy("evictionLock") EvictionIterator evictionIterator = null; // @GuardedBy("evictionLock") /* * Class loader for evictor thread to use since, in a JavaEE or similar * environment, the context class loader for the evictor thread may not have * visibility of the correct factory. See POOL-161. Uses a weak reference to * avoid potential memory leaks if the Pool is discarded rather than closed. */ private final WeakReference factoryClassLoader; // Monitoring (primarily JMX) attributes private final ObjectName objectName; private final String creationStackTrace; private final AtomicLong borrowedCount = new AtomicLong(0); private final AtomicLong returnedCount = new AtomicLong(0); final AtomicLong createdCount = new AtomicLong(0); final AtomicLong destroyedCount = new AtomicLong(0); final AtomicLong destroyedByEvictorCount = new AtomicLong(0); final AtomicLong destroyedByBorrowValidationCount = new AtomicLong(0); private final StatsStore activeTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); private final StatsStore idleTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); private final StatsStore waitTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); private final AtomicLong maxBorrowWaitTimeMillis = new AtomicLong(0L); private volatile SwallowedExceptionListener swallowedExceptionListener = null; /** * Handles JMX registration (if required) and the initialization required for * monitoring. * * @param config Pool configuration * @param jmxNameBase The default base JMX name for the new pool unless * overridden by the config * @param jmxNamePrefix Prefix to be used for JMX name for the new pool */ public BaseGenericObjectPool(final BaseObjectPoolConfig config, final String jmxNameBase, final String jmxNamePrefix) { if (config.getJmxEnabled()) { this.objectName = jmxRegister(config, jmxNameBase, jmxNamePrefix); } else { this.objectName = null; } // Populate the creation stack trace this.creationStackTrace = getStackTrace(new Exception()); // save the current TCCL (if any) to be used later by the evictor Thread final ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { factoryClassLoader = null; } else { factoryClassLoader = new WeakReference<>(cl); } fairness = config.getFairness(); } /** * Verifies that the pool is open. * @throws IllegalStateException if the pool is closed. */ final void assertOpen() throws IllegalStateException { if (isClosed()) { throw new IllegalStateException("Pool not open"); } } /** * Closes the pool, destroys the remaining idle objects and, if registered * in JMX, deregisters it. */ public abstract void close(); /** * Tries to ensure that the configured minimum number of idle instances are * available in the pool. * @throws Exception if an error occurs creating idle instances */ abstract void ensureMinIdle() throws Exception; /** *

Perform {@code numTests} idle object eviction tests, evicting * examined objects that meet the criteria for eviction. If * {@code testWhileIdle} is true, examined objects are validated * when visited (and removed if invalid); otherwise only objects that * have been idle for more than {@code minEvicableIdleTimeMillis} * are removed.

* * @throws Exception when there is a problem evicting idle objects. */ public abstract void evict() throws Exception; /** * Gets whether to block when the {@code borrowObject()} method is * invoked when the pool is exhausted (the maximum number of "active" * objects has been reached). * * @return {@code true} if {@code borrowObject()} should block * when the pool is exhausted * * @see #setBlockWhenExhausted */ public final boolean getBlockWhenExhausted() { return blockWhenExhausted; } /** * The total number of objects successfully borrowed from this pool over the * lifetime of the pool. * @return the borrowed object count */ public final long getBorrowedCount() { return borrowedCount.get(); } /** * The total number of objects created for this pool over the lifetime of * the pool. * @return the created object count */ public final long getCreatedCount() { return createdCount.get(); } /** * Provides the stack trace for the call that created this pool. JMX * registration may trigger a memory leak so it is important that pools are * deregistered when no longer used by calling the {@link #close()} method. * This method is provided to assist with identifying code that creates but * does not close it thereby creating a memory leak. * @return pool creation stack trace */ public final String getCreationStackTrace() { return creationStackTrace; } /** * The total number of objects destroyed by this pool as a result of failing * validation during {@code borrowObject()} over the lifetime of the * pool. * @return validation destroyed object count */ public final long getDestroyedByBorrowValidationCount() { return destroyedByBorrowValidationCount.get(); } /** * The total number of objects destroyed by the evictor associated with this * pool over the lifetime of the pool. * @return the evictor destroyed object count */ public final long getDestroyedByEvictorCount() { return destroyedByEvictorCount.get(); } /** * The total number of objects destroyed by this pool over the lifetime of * the pool. * @return the destroyed object count */ public final long getDestroyedCount() { return destroyedCount.get(); } /** * Gets the {@link EvictionPolicy} defined for this pool. * * @return the eviction policy * @since 2.4 * @since 2.6.0 Changed access from protected to public. */ public EvictionPolicy getEvictionPolicy() { return evictionPolicy; } /** * Gets the name of the {@link EvictionPolicy} implementation that is * used by this pool. * * @return The fully qualified class name of the {@link EvictionPolicy} * * @see #setEvictionPolicyClassName(String) */ public final String getEvictionPolicyClassName() { return evictionPolicy.getClass().getName(); } /** * Gets the timeout that will be used when waiting for the Evictor to * shutdown if this pool is closed and it is the only pool still using the * the value for the Evictor. * * @return The timeout that will be used while waiting for * the Evictor to shut down. * @since 2.10.0 */ public final Duration getEvictorShutdownTimeout() { return evictorShutdownTimeout; } /** * Gets the timeout that will be used when waiting for the Evictor to * shutdown if this pool is closed and it is the only pool still using the * the value for the Evictor. * * @return The timeout in milliseconds that will be used while waiting for * the Evictor to shut down. * @deprecated Use {@link #getEvictorShutdownTimeout()}. */ @Deprecated public final long getEvictorShutdownTimeoutMillis() { return evictorShutdownTimeout.toMillis(); } /** * Gets whether or not the pool serves threads waiting to borrow objects fairly. * True means that waiting threads are served as if waiting in a FIFO queue. * * @return {@code true} if waiting threads are to be served * by the pool in arrival order */ public final boolean getFairness() { return fairness; } /** * Provides the name under which the pool has been registered with the * platform MBean server or {@code null} if the pool has not been * registered. * @return the JMX name */ public final ObjectName getJmxName() { return objectName; } /** * Gets whether the pool has LIFO (last in, first out) behavior with * respect to idle objects - always returning the most recently used object * from the pool, or as a FIFO (first in, first out) queue, where the pool * always returns the oldest object in the idle object pool. * * @return {@code true} if the pool is configured with LIFO behavior * or {@code false} if the pool is configured with FIFO * behavior * * @see #setLifo */ public final boolean getLifo() { return lifo; } /** * The maximum time a thread has waited to borrow objects from the pool. * @return maximum wait time in milliseconds since the pool was created */ public final long getMaxBorrowWaitTimeMillis() { return maxBorrowWaitTimeMillis.get(); } /** * Gets the maximum number of objects that can be allocated by the pool * (checked out to clients, or idle awaiting checkout) at a given time. When * negative, there is no limit to the number of objects that can be * managed by the pool at one time. * * @return the cap on the total number of object instances managed by the * pool. * * @see #setMaxTotal */ public final int getMaxTotal() { return maxTotal; } /** * Gets the maximum amount of time (in milliseconds) the * {@code borrowObject()} method should block before throwing an * exception when the pool is exhausted and * {@link #getBlockWhenExhausted} is true. When less than 0, the * {@code borrowObject()} method may block indefinitely. * * @return the maximum number of milliseconds {@code borrowObject()} * will block. * * @see #setMaxWaitMillis * @see #setBlockWhenExhausted */ public final long getMaxWaitMillis() { return maxWait.toMillis(); } /** * The mean time objects are active for based on the last {@link * #MEAN_TIMING_STATS_CACHE_SIZE} objects returned to the pool. * @return mean time an object has been checked out from the pool among * recently returned objects */ public final long getMeanActiveTimeMillis() { return activeTimes.getMean(); } /** * The mean time threads wait to borrow an object based on the last {@link * #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool. * @return mean time in milliseconds that a recently served thread has had * to wait to borrow an object from the pool */ public final long getMeanBorrowWaitTimeMillis() { return waitTimes.getMean(); } /** * The mean time objects are idle for based on the last {@link * #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool. * @return mean time an object has been idle in the pool among recently * borrowed objects */ public final long getMeanIdleTimeMillis() { return idleTimes.getMean(); } /** * Gets the minimum amount of time an object may sit idle in the pool * before it is eligible for eviction by the idle object evictor (if any - * see {@link #setTimeBetweenEvictionRuns(Duration)}). When non-positive, * no objects will be evicted from the pool due to idle time alone. * * @return minimum amount of time an object may sit idle in the pool before * it is eligible for eviction * * @see #setMinEvictableIdleTimeMillis * @see #setTimeBetweenEvictionRunsMillis * @since 2.10.0 */ public final Duration getMinEvictableIdleTime() { return minEvictableIdleTime; } /** * Gets the minimum amount of time an object may sit idle in the pool * before it is eligible for eviction by the idle object evictor (if any - * see {@link #setTimeBetweenEvictionRunsMillis(long)}). When non-positive, * no objects will be evicted from the pool due to idle time alone. * * @return minimum amount of time an object may sit idle in the pool before * it is eligible for eviction * * @see #setMinEvictableIdleTimeMillis * @see #setTimeBetweenEvictionRunsMillis * @deprecated Use {@link #getMinEvictableIdleTime()}. */ @Deprecated public final long getMinEvictableIdleTimeMillis() { return minEvictableIdleTime.toMillis(); } /** * The number of instances currently idle in this pool. * @return count of instances available for checkout from the pool */ public abstract int getNumIdle(); /** * Gets the maximum number of objects to examine during each run (if any) * of the idle object evictor thread. When positive, the number of tests * performed for a run will be the minimum of the configured value and the * number of idle instances in the pool. When negative, the number of tests * performed will be ceil({@link #getNumIdle}/ * abs({@link #getNumTestsPerEvictionRun})) which means that when the * value is {@code -n} roughly one nth of the idle objects will be * tested per run. * * @return max number of objects to examine during each evictor run * * @see #setNumTestsPerEvictionRun * @see #setTimeBetweenEvictionRunsMillis */ public final int getNumTestsPerEvictionRun() { return numTestsPerEvictionRun; } /** * The total number of objects returned to this pool over the lifetime of * the pool. This excludes attempts to return the same object multiple * times. * @return the returned object count */ public final long getReturnedCount() { return returnedCount.get(); } /** * Gets the minimum amount of time an object may sit idle in the pool * before it is eligible for eviction by the idle object evictor (if any - * see {@link #setTimeBetweenEvictionRuns(Duration)}), * with the extra condition that at least {@code minIdle} object * instances remain in the pool. This setting is overridden by * {@link #getMinEvictableIdleTime} (that is, if * {@link #getMinEvictableIdleTime} is positive, then * {@link #getSoftMinEvictableIdleTime} is ignored). * * @return minimum amount of time an object may sit idle in the pool before * it is eligible for eviction if minIdle instances are available * * @see #setSoftMinEvictableIdleTime(Duration) * @since 2.10.0 */ public final Duration getSoftMinEvictableIdleTime() { return softMinEvictableIdleTime; } /** * Gets the minimum amount of time an object may sit idle in the pool * before it is eligible for eviction by the idle object evictor (if any - * see {@link #setTimeBetweenEvictionRunsMillis(long)}), * with the extra condition that at least {@code minIdle} object * instances remain in the pool. This setting is overridden by * {@link #getMinEvictableIdleTimeMillis} (that is, if * {@link #getMinEvictableIdleTimeMillis} is positive, then * {@link #getSoftMinEvictableIdleTimeMillis} is ignored). * * @return minimum amount of time an object may sit idle in the pool before * it is eligible for eviction if minIdle instances are available * * @see #setSoftMinEvictableIdleTimeMillis * @deprecated Use {@link #getSoftMinEvictableIdleTime()}. */ @Deprecated public final long getSoftMinEvictableIdleTimeMillis() { return softMinEvictableIdleTime.toMillis(); } /** * Gets the stack trace of an exception as a string. * @param e exception to trace * @return exception stack trace as a string */ private String getStackTrace(final Exception e) { // Need the exception in string form to prevent the retention of // references to classes in the stack trace that could trigger a memory // leak in a container environment. final Writer w = new StringWriter(); final PrintWriter pw = new PrintWriter(w); e.printStackTrace(pw); return w.toString(); } /** * The listener used (if any) to receive notifications of exceptions * unavoidably swallowed by the pool. * * @return The listener or {@code null} for no listener */ public final SwallowedExceptionListener getSwallowedExceptionListener() { return swallowedExceptionListener; } /** * Gets whether objects borrowed from the pool will be validated before * being returned from the {@code borrowObject()} method. Validation is * performed by the {@code validateObject()} method of the factory * associated with the pool. If the object fails to validate, it will be * removed from the pool and destroyed, and a new attempt will be made to * borrow an object from the pool. * * @return {@code true} if objects are validated before being returned * from the {@code borrowObject()} method * * @see #setTestOnBorrow */ public final boolean getTestOnBorrow() { return testOnBorrow; } /** * Gets whether objects created for the pool will be validated before * being returned from the {@code borrowObject()} method. Validation is * performed by the {@code validateObject()} method of the factory * associated with the pool. If the object fails to validate, then * {@code borrowObject()} will fail. * * @return {@code true} if newly created objects are validated before * being returned from the {@code borrowObject()} method * * @see #setTestOnCreate * * @since 2.2 */ public final boolean getTestOnCreate() { return testOnCreate; } /** * Gets whether objects borrowed from the pool will be validated when * they are returned to the pool via the {@code returnObject()} method. * Validation is performed by the {@code validateObject()} method of * the factory associated with the pool. Returning objects that fail validation * are destroyed rather then being returned the pool. * * @return {@code true} if objects are validated on return to * the pool via the {@code returnObject()} method * * @see #setTestOnReturn */ public final boolean getTestOnReturn() { return testOnReturn; } /** * Gets whether objects sitting idle in the pool will be validated by the * idle object evictor (if any - see * {@link #setTimeBetweenEvictionRuns(Duration)}). Validation is performed * by the {@code validateObject()} method of the factory associated * with the pool. If the object fails to validate, it will be removed from * the pool and destroyed. * * @return {@code true} if objects will be validated by the evictor * * @see #setTestWhileIdle * @see #setTimeBetweenEvictionRunsMillis */ public final boolean getTestWhileIdle() { return testWhileIdle; } /** * Gets the duration to sleep between runs of the idle * object evictor thread. When non-positive, no idle object evictor thread * will be run. * * @return number of milliseconds to sleep between evictor runs * * @see #setTimeBetweenEvictionRuns * @since 2.10.0 */ public final Duration getTimeBetweenEvictionRuns() { return timeBetweenEvictionRuns; } /** * Gets the number of milliseconds to sleep between runs of the idle * object evictor thread. When non-positive, no idle object evictor thread * will be run. * * @return number of milliseconds to sleep between evictor runs * * @see #setTimeBetweenEvictionRunsMillis * @deprecated Use {@link #getTimeBetweenEvictionRuns()}. */ @Deprecated public final long getTimeBetweenEvictionRunsMillis() { return timeBetweenEvictionRuns.toMillis(); } /** * Has this pool instance been closed. * @return {@code true} when this pool has been closed. */ public final boolean isClosed() { return closed; } /** * Registers the pool with the platform MBean server. * The registered name will be * {@code jmxNameBase + jmxNamePrefix + i} where i is the least * integer greater than or equal to 1 such that the name is not already * registered. Swallows MBeanRegistrationException, NotCompliantMBeanException * returning null. * * @param config Pool configuration * @param jmxNameBase default base JMX name for this pool * @param jmxNamePrefix name prefix * @return registered ObjectName, null if registration fails */ private ObjectName jmxRegister(final BaseObjectPoolConfig config, final String jmxNameBase, String jmxNamePrefix) { ObjectName newObjectName = null; final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); int i = 1; boolean registered = false; String base = config.getJmxNameBase(); if (base == null) { base = jmxNameBase; } while (!registered) { try { ObjectName objName; // Skip the numeric suffix for the first pool in case there is // only one so the names are cleaner. if (i == 1) { objName = new ObjectName(base + jmxNamePrefix); } else { objName = new ObjectName(base + jmxNamePrefix + i); } mbs.registerMBean(this, objName); newObjectName = objName; registered = true; } catch (final MalformedObjectNameException e) { if (BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX.equals( jmxNamePrefix) && jmxNameBase.equals(base)) { // Shouldn't happen. Skip registration if it does. registered = true; } else { // Must be an invalid name. Use the defaults instead. jmxNamePrefix = BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX; base = jmxNameBase; } } catch (final InstanceAlreadyExistsException e) { // Increment the index and try again i++; } catch (final MBeanRegistrationException | NotCompliantMBeanException e) { // Shouldn't happen. Skip registration if it does. registered = true; } } return newObjectName; } /** * Unregisters this pool's MBean. */ final void jmxUnregister() { if (objectName != null) { try { ManagementFactory.getPlatformMBeanServer().unregisterMBean( objectName); } catch (final MBeanRegistrationException | InstanceNotFoundException e) { swallowException(e); } } } /** * Marks the object as returning to the pool. * @param pooledObject instance to return to the keyed pool */ protected void markReturningState(final PooledObject pooledObject) { synchronized(pooledObject) { final PooledObjectState state = pooledObject.getState(); if (state != PooledObjectState.ALLOCATED) { throw new IllegalStateException( "Object has already been returned to this pool or is invalid"); } pooledObject.markReturning(); // Keep from being marked abandoned } } /** * Sets whether to block when the {@code borrowObject()} method is * invoked when the pool is exhausted (the maximum number of "active" * objects has been reached). * * @param blockWhenExhausted {@code true} if * {@code borrowObject()} should block * when the pool is exhausted * * @see #getBlockWhenExhausted */ public final void setBlockWhenExhausted(final boolean blockWhenExhausted) { this.blockWhenExhausted = blockWhenExhausted; } /** * Initializes the receiver with the given configuration. * * @param config Initialization source. */ protected void setConfig(final BaseObjectPoolConfig config) { setLifo(config.getLifo()); setMaxWaitMillis(config.getMaxWaitMillis()); setBlockWhenExhausted(config.getBlockWhenExhausted()); setTestOnCreate(config.getTestOnCreate()); setTestOnBorrow(config.getTestOnBorrow()); setTestOnReturn(config.getTestOnReturn()); setTestWhileIdle(config.getTestWhileIdle()); setNumTestsPerEvictionRun(config.getNumTestsPerEvictionRun()); setMinEvictableIdleTime(config.getMinEvictableIdleTime()); setTimeBetweenEvictionRuns(config.getTimeBetweenEvictionRuns()); setSoftMinEvictableIdleTime(config.getSoftMinEvictableIdleTime()); final EvictionPolicy policy = config.getEvictionPolicy(); if (policy == null) { // Use the class name (pre-2.6.0 compatible) setEvictionPolicyClassName(config.getEvictionPolicyClassName()); } else { // Otherwise, use the class (2.6.0 feature) setEvictionPolicy(policy); } setEvictorShutdownTimeout(config.getEvictorShutdownTimeout()); } // Monitoring (primarily JMX) related methods /** * Sets the eviction policy for this pool. * * @param evictionPolicy * the eviction policy for this pool. * @since 2.6.0 */ public void setEvictionPolicy(final EvictionPolicy evictionPolicy) { this.evictionPolicy = evictionPolicy; } /** * Sets the eviction policy. * * @param className Eviction policy class name. * @param classLoader Load the class from this class loader. */ @SuppressWarnings("unchecked") private void setEvictionPolicy(final String className, final ClassLoader classLoader) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { final Class clazz = Class.forName(className, true, classLoader); final Object policy = clazz.getConstructor().newInstance(); this.evictionPolicy = (EvictionPolicy) policy; } /** * Sets the name of the {@link EvictionPolicy} implementation that is used by this pool. The Pool will attempt to * load the class using the thread context class loader. If that fails, the use the class loader for the * {@link EvictionPolicy} interface. * * @param evictionPolicyClassName * the fully qualified class name of the new eviction policy * * @see #getEvictionPolicyClassName() * @since 2.6.0 If loading the class using the thread context class loader fails, use the class loader for the * {@link EvictionPolicy} interface. */ public final void setEvictionPolicyClassName(final String evictionPolicyClassName) { setEvictionPolicyClassName(evictionPolicyClassName, Thread.currentThread().getContextClassLoader()); } /** * Sets the name of the {@link EvictionPolicy} implementation that is used by this pool. The Pool will attempt to * load the class using the given class loader. If that fails, use the class loader for the {@link EvictionPolicy} * interface. * * @param evictionPolicyClassName * the fully qualified class name of the new eviction policy * @param classLoader * the class loader to load the given {@code evictionPolicyClassName}. * * @see #getEvictionPolicyClassName() * @since 2.6.0 If loading the class using the given class loader fails, use the class loader for the * {@link EvictionPolicy} interface. */ public final void setEvictionPolicyClassName(final String evictionPolicyClassName, final ClassLoader classLoader) { // Getting epClass here and now best matches the caller's environment final Class epClass = EvictionPolicy.class; final ClassLoader epClassLoader = epClass.getClassLoader(); try { try { setEvictionPolicy(evictionPolicyClassName, classLoader); } catch (final ClassCastException | ClassNotFoundException e) { setEvictionPolicy(evictionPolicyClassName, epClassLoader); } } catch (final ClassCastException e) { throw new IllegalArgumentException("Class " + evictionPolicyClassName + " from class loaders [" + classLoader + ", " + epClassLoader + "] do not implement " + EVICTION_POLICY_TYPE_NAME); } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { final String exMessage = "Unable to create " + EVICTION_POLICY_TYPE_NAME + " instance of type " + evictionPolicyClassName; throw new IllegalArgumentException(exMessage, e); } } /** * Sets the timeout that will be used when waiting for the Evictor to shutdown if this pool is closed and it is the * only pool still using the the value for the Evictor. * * @param evictorShutdownTimeoutMillis the timeout in milliseconds that will be used while waiting for the Evictor * to shut down. * @since 2.10.0 */ public final void setEvictorShutdownTimeout(final Duration evictorShutdownTimeoutMillis) { this.evictorShutdownTimeout = evictorShutdownTimeoutMillis; } /** * Sets the timeout that will be used when waiting for the Evictor to shutdown if this pool is closed and it is the * only pool still using the the value for the Evictor. * * @param evictorShutdownTimeoutMillis the timeout in milliseconds that will be used while waiting for the Evictor * to shut down. * @deprecated Use {@link #setEvictorShutdownTimeout(Duration)}. */ @Deprecated public final void setEvictorShutdownTimeoutMillis(final long evictorShutdownTimeoutMillis) { this.evictorShutdownTimeout = Duration.ofMillis(evictorShutdownTimeoutMillis); } /** * Sets whether the pool has LIFO (last in, first out) behavior with * respect to idle objects - always returning the most recently used object * from the pool, or as a FIFO (first in, first out) queue, where the pool * always returns the oldest object in the idle object pool. * * @param lifo {@code true} if the pool is to be configured with LIFO * behavior or {@code false} if the pool is to be * configured with FIFO behavior * * @see #getLifo() */ public final void setLifo(final boolean lifo) { this.lifo = lifo; } /** * Sets the cap on the number of objects that can be allocated by the pool * (checked out to clients, or idle awaiting checkout) at a given time. Use * a negative value for no limit. * * @param maxTotal The cap on the total number of object instances managed * by the pool. Negative values mean that there is no limit * to the number of objects allocated by the pool. * * @see #getMaxTotal */ public final void setMaxTotal(final int maxTotal) { this.maxTotal = maxTotal; } /** * Sets the maximum amount of time (in milliseconds) the * {@code borrowObject()} method should block before throwing an * exception when the pool is exhausted and * {@link #getBlockWhenExhausted} is true. When less than 0, the * {@code borrowObject()} method may block indefinitely. * * @param maxWaitMillis the maximum number of milliseconds * {@code borrowObject()} will block or negative * for indefinitely. * * @see #getMaxWaitMillis * @see #setBlockWhenExhausted */ public final void setMaxWaitMillis(final long maxWaitMillis) { this.maxWait = Duration.ofMillis(maxWaitMillis); } /** * Sets the minimum amount of time an object may sit idle in the pool * before it is eligible for eviction by the idle object evictor (if any - * see {@link #setTimeBetweenEvictionRuns(Duration)}). When non-positive, * no objects will be evicted from the pool due to idle time alone. * * @param minEvictableIdleTimeMillis * minimum amount of time an object may sit idle in the pool * before it is eligible for eviction * * @see #getMinEvictableIdleTime * @see #setTimeBetweenEvictionRuns * @since 2.10.0 */ public final void setMinEvictableIdleTime(final Duration minEvictableIdleTimeMillis) { this.minEvictableIdleTime = minEvictableIdleTimeMillis; } /** * Sets the minimum amount of time an object may sit idle in the pool * before it is eligible for eviction by the idle object evictor (if any - * see {@link #setTimeBetweenEvictionRunsMillis(long)}). When non-positive, * no objects will be evicted from the pool due to idle time alone. * * @param minEvictableIdleTimeMillis * minimum amount of time an object may sit idle in the pool * before it is eligible for eviction * * @see #getMinEvictableIdleTimeMillis * @see #setTimeBetweenEvictionRunsMillis * @deprecated Use {@link #setMinEvictableIdleTime(Duration)}. */ @Deprecated public final void setMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) { this.minEvictableIdleTime = Duration.ofMillis(minEvictableIdleTimeMillis); } /** * Sets the maximum number of objects to examine during each run (if any) * of the idle object evictor thread. When positive, the number of tests * performed for a run will be the minimum of the configured value and the * number of idle instances in the pool. When negative, the number of tests * performed will be ceil({@link #getNumIdle}/ * abs({@link #getNumTestsPerEvictionRun})) which means that when the * value is {@code -n} roughly one nth of the idle objects will be * tested per run. * * @param numTestsPerEvictionRun * max number of objects to examine during each evictor run * * @see #getNumTestsPerEvictionRun * @see #setTimeBetweenEvictionRunsMillis */ public final void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) { this.numTestsPerEvictionRun = numTestsPerEvictionRun; } /** * Sets the minimum amount of time an object may sit idle in the pool * before it is eligible for eviction by the idle object evictor (if any - * see {@link #setTimeBetweenEvictionRuns(Duration)}), * with the extra condition that at least {@code minIdle} object * instances remain in the pool. This setting is overridden by * {@link #getMinEvictableIdleTime} (that is, if * {@link #getMinEvictableIdleTime} is positive, then * {@link #getSoftMinEvictableIdleTime} is ignored). * * @param softMinEvictableIdleTime * minimum amount of time an object may sit idle in the pool * before it is eligible for eviction if minIdle instances are * available * * @see #getSoftMinEvictableIdleTimeMillis * @since 2.10.0 */ public final void setSoftMinEvictableIdleTime(final Duration softMinEvictableIdleTime) { this.softMinEvictableIdleTime = softMinEvictableIdleTime; } /** * Sets the minimum amount of time an object may sit idle in the pool * before it is eligible for eviction by the idle object evictor (if any - * see {@link #setTimeBetweenEvictionRunsMillis(long)}), * with the extra condition that at least {@code minIdle} object * instances remain in the pool. This setting is overridden by * {@link #getMinEvictableIdleTimeMillis} (that is, if * {@link #getMinEvictableIdleTimeMillis} is positive, then * {@link #getSoftMinEvictableIdleTimeMillis} is ignored). * * @param softMinEvictableIdleTimeMillis * minimum amount of time an object may sit idle in the pool * before it is eligible for eviction if minIdle instances are * available * * @see #getSoftMinEvictableIdleTimeMillis * @deprecated Use {@link #setSoftMinEvictableIdleTime(Duration)}. */ @Deprecated public final void setSoftMinEvictableIdleTimeMillis(final long softMinEvictableIdleTimeMillis) { this.softMinEvictableIdleTime = Duration.ofMillis(softMinEvictableIdleTimeMillis); } /** * The listener used (if any) to receive notifications of exceptions * unavoidably swallowed by the pool. * * @param swallowedExceptionListener The listener or {@code null} * for no listener */ public final void setSwallowedExceptionListener( final SwallowedExceptionListener swallowedExceptionListener) { this.swallowedExceptionListener = swallowedExceptionListener; } /** * Sets whether objects borrowed from the pool will be validated before * being returned from the {@code borrowObject()} method. Validation is * performed by the {@code validateObject()} method of the factory * associated with the pool. If the object fails to validate, it will be * removed from the pool and destroyed, and a new attempt will be made to * borrow an object from the pool. * * @param testOnBorrow {@code true} if objects should be validated * before being returned from the * {@code borrowObject()} method * * @see #getTestOnBorrow */ public final void setTestOnBorrow(final boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; } /** * Sets whether objects created for the pool will be validated before * being returned from the {@code borrowObject()} method. Validation is * performed by the {@code validateObject()} method of the factory * associated with the pool. If the object fails to validate, then * {@code borrowObject()} will fail. * * @param testOnCreate {@code true} if newly created objects should be * validated before being returned from the * {@code borrowObject()} method * * @see #getTestOnCreate * * @since 2.2 */ public final void setTestOnCreate(final boolean testOnCreate) { this.testOnCreate = testOnCreate; } /** * Sets whether objects borrowed from the pool will be validated when * they are returned to the pool via the {@code returnObject()} method. * Validation is performed by the {@code validateObject()} method of * the factory associated with the pool. Returning objects that fail validation * are destroyed rather then being returned the pool. * * @param testOnReturn {@code true} if objects are validated on * return to the pool via the * {@code returnObject()} method * * @see #getTestOnReturn */ public final void setTestOnReturn(final boolean testOnReturn) { this.testOnReturn = testOnReturn; } /** * Sets whether objects sitting idle in the pool will be validated by the * idle object evictor (if any - see * {@link #setTimeBetweenEvictionRuns(Duration)}). Validation is performed * by the {@code validateObject()} method of the factory associated * with the pool. If the object fails to validate, it will be removed from * the pool and destroyed. Note that setting this property has no effect * unless the idle object evictor is enabled by setting * {@code timeBetweenEvictionRunsMillis} to a positive value. * * @param testWhileIdle * {@code true} so objects will be validated by the evictor * * @see #getTestWhileIdle * @see #setTimeBetweenEvictionRuns */ public final void setTestWhileIdle(final boolean testWhileIdle) { this.testWhileIdle = testWhileIdle; } /** * Sets the number of milliseconds to sleep between runs of the idle object evictor thread. *
    *
  • When positive, the idle object evictor thread starts.
  • *
  • When non-positive, no idle object evictor thread runs.
  • *
* * @param timeBetweenEvictionRuns * duration to sleep between evictor runs * * @see #getTimeBetweenEvictionRunsMillis * @since 2.10.0 */ public final void setTimeBetweenEvictionRuns(final Duration timeBetweenEvictionRuns) { this.timeBetweenEvictionRuns = timeBetweenEvictionRuns; startEvictor(this.timeBetweenEvictionRuns); } /** * Sets the number of milliseconds to sleep between runs of the idle object evictor thread. *
    *
  • When positive, the idle object evictor thread starts.
  • *
  • When non-positive, no idle object evictor thread runs.
  • *
* * @param timeBetweenEvictionRunsMillis * number of milliseconds to sleep between evictor runs * * @see #getTimeBetweenEvictionRunsMillis * @deprecated Use {@link #setTimeBetweenEvictionRuns(Duration)}. */ @Deprecated public final void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRunsMillis); startEvictor(timeBetweenEvictionRuns); } /** *

Starts the evictor with the given delay. If there is an evictor * running when this method is called, it is stopped and replaced with a * new evictor with the specified delay.

* *

This method needs to be final, since it is called from a constructor. * See POOL-195.

* * @param delay time in milliseconds before start and between eviction runs */ final void startEvictor(final Duration delay) { synchronized (evictionLock) { final boolean isPositiverDelay = PoolImplUtils.isPositive(delay); if (evictor == null) { // Starting evictor for the first time or after a cancel if (isPositiverDelay) { // Starting new evictor evictor = new Evictor(); EvictionTimer.schedule(evictor, delay, delay); } } else { // Stop or restart of existing evictor if (isPositiverDelay) { // Restart synchronized (EvictionTimer.class) { // Ensure no cancel can happen between cancel / schedule calls EvictionTimer.cancel(evictor, evictorShutdownTimeout, true); evictor = null; evictionIterator = null; evictor = new Evictor(); EvictionTimer.schedule(evictor, delay, delay); } } else { // Stopping evictor EvictionTimer.cancel(evictor, evictorShutdownTimeout, false); } } } } // Inner classes /** * Stops the evictor. */ void stopEvictor() { startEvictor(Duration.ofMillis(-1L)); } /** * Swallows an exception and notifies the configured listener for swallowed * exceptions queue. * * @param swallowException exception to be swallowed */ final void swallowException(final Exception swallowException) { final SwallowedExceptionListener listener = getSwallowedExceptionListener(); if (listener == null) { return; } try { listener.onSwallowException(swallowException); } catch (final VirtualMachineError e) { throw e; } catch (final Throwable t) { // Ignore. Enjoy the irony. } } @Override protected void toStringAppendFields(final StringBuilder builder) { builder.append("maxTotal="); builder.append(maxTotal); builder.append(", blockWhenExhausted="); builder.append(blockWhenExhausted); builder.append(", maxWaitMillis="); builder.append(maxWait); builder.append(", lifo="); builder.append(lifo); builder.append(", fairness="); builder.append(fairness); builder.append(", testOnCreate="); builder.append(testOnCreate); builder.append(", testOnBorrow="); builder.append(testOnBorrow); builder.append(", testOnReturn="); builder.append(testOnReturn); builder.append(", testWhileIdle="); builder.append(testWhileIdle); builder.append(", timeBetweenEvictionRunsMillis="); builder.append(timeBetweenEvictionRuns); builder.append(", numTestsPerEvictionRun="); builder.append(numTestsPerEvictionRun); builder.append(", minEvictableIdleTimeMillis="); builder.append(minEvictableIdleTime); builder.append(", softMinEvictableIdleTimeMillis="); builder.append(softMinEvictableIdleTime); builder.append(", evictionPolicy="); builder.append(evictionPolicy); builder.append(", closeLock="); builder.append(closeLock); builder.append(", closed="); builder.append(closed); builder.append(", evictionLock="); builder.append(evictionLock); builder.append(", evictor="); builder.append(evictor); builder.append(", evictionIterator="); builder.append(evictionIterator); builder.append(", factoryClassLoader="); builder.append(factoryClassLoader); builder.append(", oname="); builder.append(objectName); builder.append(", creationStackTrace="); builder.append(creationStackTrace); builder.append(", borrowedCount="); builder.append(borrowedCount); builder.append(", returnedCount="); builder.append(returnedCount); builder.append(", createdCount="); builder.append(createdCount); builder.append(", destroyedCount="); builder.append(destroyedCount); builder.append(", destroyedByEvictorCount="); builder.append(destroyedByEvictorCount); builder.append(", destroyedByBorrowValidationCount="); builder.append(destroyedByBorrowValidationCount); builder.append(", activeTimes="); builder.append(activeTimes); builder.append(", idleTimes="); builder.append(idleTimes); builder.append(", waitTimes="); builder.append(waitTimes); builder.append(", maxBorrowWaitTimeMillis="); builder.append(maxBorrowWaitTimeMillis); builder.append(", swallowedExceptionListener="); builder.append(swallowedExceptionListener); } /** * Updates statistics after an object is borrowed from the pool. * * @param p object borrowed from the pool * @param waitTime time (in milliseconds) that the borrowing thread had to wait */ final void updateStatsBorrow(final PooledObject p, final Duration waitTime) { borrowedCount.incrementAndGet(); idleTimes.add(p.getIdleTime()); waitTimes.add(waitTime); final long waitTimeMillis = waitTime.toMillis(); // lock-free optimistic-locking maximum long currentMaxMillis; do { currentMaxMillis = maxBorrowWaitTimeMillis.get(); if (currentMaxMillis >= waitTimeMillis) { break; } } while (!maxBorrowWaitTimeMillis.compareAndSet(currentMaxMillis, waitTimeMillis)); } /** * Updates statistics after an object is returned to the pool. * * @param activeTime the amount of time (in milliseconds) that the returning * object was checked out */ final void updateStatsReturn(final Duration activeTime) { returnedCount.incrementAndGet(); activeTimes.add(activeTime); } } BaseObjectPoolConfig.java000066400000000000000000001030131405425132200342700ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.time.Duration; import org.apache.commons.pool2.BaseObject; /** * Provides the implementation for the common attributes shared by the * sub-classes. New instances of this class will be created using the defaults * defined by the public constants. *

* This class is not thread-safe. *

* * @param Type of element pooled. * @since 2.0 */ public abstract class BaseObjectPoolConfig extends BaseObject implements Cloneable { /** * The default value for the {@code lifo} configuration attribute. * @see GenericObjectPool#getLifo() * @see GenericKeyedObjectPool#getLifo() */ public static final boolean DEFAULT_LIFO = true; /** * The default value for the {@code fairness} configuration attribute. * @see GenericObjectPool#getFairness() * @see GenericKeyedObjectPool#getFairness() */ public static final boolean DEFAULT_FAIRNESS = false; /** * The default value for the {@code maxWait} configuration attribute. * @see GenericObjectPool#getMaxWaitMillis() * @see GenericKeyedObjectPool#getMaxWaitMillis() */ public static final long DEFAULT_MAX_WAIT_MILLIS = -1L; /** * The default value for the {@code maxWait} configuration attribute. * @see GenericObjectPool#getMaxWaitMillis() * @see GenericKeyedObjectPool#getMaxWaitMillis() * @since 2.10.0 */ public static final Duration DEFAULT_MAX_WAIT = Duration.ofMillis(DEFAULT_MAX_WAIT_MILLIS); /** * The default value for the {@code minEvictableIdleTime} * configuration attribute. * @see GenericObjectPool#getMinEvictableIdleTimeMillis() * @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis() * @deprecated Use {@link #DEFAULT_MIN_EVICTABLE_IDLE_TIME}. */ @Deprecated public static final long DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 1000L * 60L * 30L; /** * The default value for the {@code minEvictableIdleTime} * configuration attribute. * @see GenericObjectPool#getMinEvictableIdleTimeMillis() * @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis() * @since 2.10.0 */ public static final Duration DEFAULT_MIN_EVICTABLE_IDLE_TIME = Duration.ofMillis(DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS); /** * The default value for the {@code softMinEvictableIdleTime} * configuration attribute. * @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis() * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis() * @deprecated Use {@link #DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME}. */ @Deprecated public static final long DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = -1; /** * The default value for the {@code softMinEvictableIdleTime} * configuration attribute. * @see GenericObjectPool#getSoftMinEvictableIdleTime() * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTime() * @since 2.10.0 */ public static final Duration DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME = Duration.ofMillis(DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS); /** * The default value for {@code evictorShutdownTimeout} configuration * attribute. * @see GenericObjectPool#getEvictorShutdownTimeoutMillis() * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis() * @deprecated Use {@link #DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT}. */ @Deprecated public static final long DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS = 10L * 1000L; /** * The default value for {@code evictorShutdownTimeout} configuration * attribute. * @see GenericObjectPool#getEvictorShutdownTimeout() * @see GenericKeyedObjectPool#getEvictorShutdownTimeout() * @since 2.10.0 */ public static final Duration DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT = Duration.ofMillis(DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS); /** * The default value for the {@code numTestsPerEvictionRun} configuration * attribute. * @see GenericObjectPool#getNumTestsPerEvictionRun() * @see GenericKeyedObjectPool#getNumTestsPerEvictionRun() */ public static final int DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3; /** * The default value for the {@code testOnCreate} configuration attribute. * @see GenericObjectPool#getTestOnCreate() * @see GenericKeyedObjectPool#getTestOnCreate() * * @since 2.2 */ public static final boolean DEFAULT_TEST_ON_CREATE = false; /** * The default value for the {@code testOnBorrow} configuration attribute. * @see GenericObjectPool#getTestOnBorrow() * @see GenericKeyedObjectPool#getTestOnBorrow() */ public static final boolean DEFAULT_TEST_ON_BORROW = false; /** * The default value for the {@code testOnReturn} configuration attribute. * @see GenericObjectPool#getTestOnReturn() * @see GenericKeyedObjectPool#getTestOnReturn() */ public static final boolean DEFAULT_TEST_ON_RETURN = false; /** * The default value for the {@code testWhileIdle} configuration attribute. * @see GenericObjectPool#getTestWhileIdle() * @see GenericKeyedObjectPool#getTestWhileIdle() */ public static final boolean DEFAULT_TEST_WHILE_IDLE = false; /** * The default value for the {@code timeBetweenEvictionRuns} * configuration attribute. * @see GenericObjectPool#getTimeBetweenEvictionRunsMillis() * @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis() * @deprecated Use {@link #DEFAULT_TIME_BETWEEN_EVICTION_RUNS}. */ @Deprecated public static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L; /** * The default value for the {@code timeBetweenEvictionRuns} * configuration attribute. * @see GenericObjectPool#getTimeBetweenEvictionRunsMillis() * @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis() */ public static final Duration DEFAULT_TIME_BETWEEN_EVICTION_RUNS = Duration.ofMillis(DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS); /** * The default value for the {@code blockWhenExhausted} configuration * attribute. * @see GenericObjectPool#getBlockWhenExhausted() * @see GenericKeyedObjectPool#getBlockWhenExhausted() */ public static final boolean DEFAULT_BLOCK_WHEN_EXHAUSTED = true; /** * The default value for enabling JMX for pools created with a configuration * instance. */ public static final boolean DEFAULT_JMX_ENABLE = true; /** * The default value for the prefix used to name JMX enabled pools created * with a configuration instance. * @see GenericObjectPool#getJmxName() * @see GenericKeyedObjectPool#getJmxName() */ public static final String DEFAULT_JMX_NAME_PREFIX = "pool"; /** * The default value for the base name to use to name JMX enabled pools * created with a configuration instance. The default is {@code null} * which means the pool will provide the base name to use. * @see GenericObjectPool#getJmxName() * @see GenericKeyedObjectPool#getJmxName() */ public static final String DEFAULT_JMX_NAME_BASE = null; /** * The default value for the {@code evictionPolicyClassName} configuration * attribute. * @see GenericObjectPool#getEvictionPolicyClassName() * @see GenericKeyedObjectPool#getEvictionPolicyClassName() */ public static final String DEFAULT_EVICTION_POLICY_CLASS_NAME = DefaultEvictionPolicy.class.getName(); private boolean lifo = DEFAULT_LIFO; private boolean fairness = DEFAULT_FAIRNESS; private Duration maxWaitMillis = DEFAULT_MAX_WAIT; private Duration minEvictableIdleTime = DEFAULT_MIN_EVICTABLE_IDLE_TIME; private Duration evictorShutdownTimeout = DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT; private Duration softMinEvictableIdleTime = DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME; private int numTestsPerEvictionRun = DEFAULT_NUM_TESTS_PER_EVICTION_RUN; private EvictionPolicy evictionPolicy; // Only 2.6.0 applications set this private String evictionPolicyClassName = DEFAULT_EVICTION_POLICY_CLASS_NAME; private boolean testOnCreate = DEFAULT_TEST_ON_CREATE; private boolean testOnBorrow = DEFAULT_TEST_ON_BORROW; private boolean testOnReturn = DEFAULT_TEST_ON_RETURN; private boolean testWhileIdle = DEFAULT_TEST_WHILE_IDLE; private Duration timeBetweenEvictionRuns = DEFAULT_TIME_BETWEEN_EVICTION_RUNS; private boolean blockWhenExhausted = DEFAULT_BLOCK_WHEN_EXHAUSTED; private boolean jmxEnabled = DEFAULT_JMX_ENABLE; // TODO Consider changing this to a single property for 3.x private String jmxNamePrefix = DEFAULT_JMX_NAME_PREFIX; private String jmxNameBase = DEFAULT_JMX_NAME_BASE; /** * Gets the value for the {@code blockWhenExhausted} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code blockWhenExhausted} for this * configuration instance * * @see GenericObjectPool#getBlockWhenExhausted() * @see GenericKeyedObjectPool#getBlockWhenExhausted() */ public boolean getBlockWhenExhausted() { return blockWhenExhausted; } /** * Gets the value for the {@code evictionPolicyClass} configuration * attribute for pools created with this configuration instance. * * @return The current setting of {@code evictionPolicyClass} for this * configuration instance * * @see GenericObjectPool#getEvictionPolicy() * @see GenericKeyedObjectPool#getEvictionPolicy() * @since 2.6.0 */ public EvictionPolicy getEvictionPolicy() { return evictionPolicy; } /** * Gets the value for the {@code evictionPolicyClassName} configuration * attribute for pools created with this configuration instance. * * @return The current setting of {@code evictionPolicyClassName} for this * configuration instance * * @see GenericObjectPool#getEvictionPolicyClassName() * @see GenericKeyedObjectPool#getEvictionPolicyClassName() */ public String getEvictionPolicyClassName() { return evictionPolicyClassName; } /** * Gets the value for the {@code evictorShutdownTimeout} configuration * attribute for pools created with this configuration instance. * * @return The current setting of {@code evictorShutdownTimeout} for * this configuration instance * * @see GenericObjectPool#getEvictorShutdownTimeout() * @see GenericKeyedObjectPool#getEvictorShutdownTimeout() * @since 2.10.0 */ public Duration getEvictorShutdownTimeout() { return evictorShutdownTimeout; } /** * Gets the value for the {@code evictorShutdownTimeout} configuration * attribute for pools created with this configuration instance. * * @return The current setting of {@code evictorShutdownTimeout} for * this configuration instance * * @see GenericObjectPool#getEvictorShutdownTimeoutMillis() * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis() * @deprecated Use {@link #getEvictorShutdownTimeout()}. */ @Deprecated public long getEvictorShutdownTimeoutMillis() { return evictorShutdownTimeout.toMillis(); } /** * Gets the value for the {@code fairness} configuration attribute for pools * created with this configuration instance. * * @return The current setting of {@code fairness} for this configuration * instance * * @see GenericObjectPool#getFairness() * @see GenericKeyedObjectPool#getFairness() */ public boolean getFairness() { return fairness; } /** * Gets the value of the flag that determines if JMX will be enabled for * pools created with this configuration instance. * * @return The current setting of {@code jmxEnabled} for this configuration * instance */ public boolean getJmxEnabled() { return jmxEnabled; } /** * Gets the value of the JMX name base that will be used as part of the * name assigned to JMX enabled pools created with this configuration * instance. A value of {@code null} means that the pool will define * the JMX name base. * * @return The current setting of {@code jmxNameBase} for this * configuration instance */ public String getJmxNameBase() { return jmxNameBase; } /** * Gets the value of the JMX name prefix that will be used as part of the * name assigned to JMX enabled pools created with this configuration * instance. * * @return The current setting of {@code jmxNamePrefix} for this * configuration instance */ public String getJmxNamePrefix() { return jmxNamePrefix; } /** * Gets the value for the {@code lifo} configuration attribute for pools * created with this configuration instance. * * @return The current setting of {@code lifo} for this configuration * instance * * @see GenericObjectPool#getLifo() * @see GenericKeyedObjectPool#getLifo() */ public boolean getLifo() { return lifo; } /** * Gets the value for the {@code maxWait} configuration attribute for pools * created with this configuration instance. * * @return The current setting of {@code maxWait} for this * configuration instance * * @see GenericObjectPool#getMaxWaitMillis() * @see GenericKeyedObjectPool#getMaxWaitMillis() */ public long getMaxWaitMillis() { return maxWaitMillis.toMillis(); } /** * Gets the value for the {@code minEvictableIdleTime} configuration * attribute for pools created with this configuration instance. * * @return The current setting of {@code minEvictableIdleTime} for * this configuration instance * * @see GenericObjectPool#getMinEvictableIdleTime() * @see GenericKeyedObjectPool#getMinEvictableIdleTime() * @since 2.10.0 */ public Duration getMinEvictableIdleTime() { return minEvictableIdleTime; } /** * Gets the value for the {@code minEvictableIdleTime} configuration * attribute for pools created with this configuration instance. * * @return The current setting of {@code minEvictableIdleTime} for * this configuration instance * * @see GenericObjectPool#getMinEvictableIdleTimeMillis() * @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis() * @deprecated Use {@link #getMinEvictableIdleTime()}. */ @Deprecated public long getMinEvictableIdleTimeMillis() { return minEvictableIdleTime.toMillis(); } /** * Gets the value for the {@code numTestsPerEvictionRun} configuration * attribute for pools created with this configuration instance. * * @return The current setting of {@code numTestsPerEvictionRun} for this * configuration instance * * @see GenericObjectPool#getNumTestsPerEvictionRun() * @see GenericKeyedObjectPool#getNumTestsPerEvictionRun() */ public int getNumTestsPerEvictionRun() { return numTestsPerEvictionRun; } /** * Gets the value for the {@code softMinEvictableIdleTime} * configuration attribute for pools created with this configuration * instance. * * @return The current setting of {@code softMinEvictableIdleTime} * for this configuration instance * * @see GenericObjectPool#getSoftMinEvictableIdleTime() * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTime() * @since 2.10.0 */ public Duration getSoftMinEvictableIdleTime() { return softMinEvictableIdleTime; } /** * Gets the value for the {@code softMinEvictableIdleTime} * configuration attribute for pools created with this configuration * instance. * * @return The current setting of {@code softMinEvictableIdleTime} * for this configuration instance * * @see GenericObjectPool#getSoftMinEvictableIdleTime() * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTime() */ @Deprecated public long getSoftMinEvictableIdleTimeMillis() { return softMinEvictableIdleTime.toMillis(); } /** * Gets the value for the {@code testOnBorrow} configuration attribute for * pools created with this configuration instance. * * @return The current setting of {@code testOnBorrow} for this * configuration instance * * @see GenericObjectPool#getTestOnBorrow() * @see GenericKeyedObjectPool#getTestOnBorrow() */ public boolean getTestOnBorrow() { return testOnBorrow; } /** * Gets the value for the {@code testOnCreate} configuration attribute for * pools created with this configuration instance. * * @return The current setting of {@code testOnCreate} for this * configuration instance * * @see GenericObjectPool#getTestOnCreate() * @see GenericKeyedObjectPool#getTestOnCreate() * * @since 2.2 */ public boolean getTestOnCreate() { return testOnCreate; } /** * Gets the value for the {@code testOnReturn} configuration attribute for * pools created with this configuration instance. * * @return The current setting of {@code testOnReturn} for this * configuration instance * * @see GenericObjectPool#getTestOnReturn() * @see GenericKeyedObjectPool#getTestOnReturn() */ public boolean getTestOnReturn() { return testOnReturn; } /** * Gets the value for the {@code testWhileIdle} configuration attribute for * pools created with this configuration instance. * * @return The current setting of {@code testWhileIdle} for this * configuration instance * * @see GenericObjectPool#getTestWhileIdle() * @see GenericKeyedObjectPool#getTestWhileIdle() */ public boolean getTestWhileIdle() { return testWhileIdle; } /** * Gets the value for the {@code timeBetweenEvictionRuns} configuration * attribute for pools created with this configuration instance. * * @return The current setting of {@code timeBetweenEvictionRuns} for * this configuration instance * * @see GenericObjectPool#getTimeBetweenEvictionRuns() * @see GenericKeyedObjectPool#getTimeBetweenEvictionRuns() * @since 2.10.0 */ public Duration getTimeBetweenEvictionRuns() { return timeBetweenEvictionRuns; } /** * Gets the value for the {@code timeBetweenEvictionRuns} configuration * attribute for pools created with this configuration instance. * * @return The current setting of {@code timeBetweenEvictionRuns} for * this configuration instance * * @see GenericObjectPool#getTimeBetweenEvictionRunsMillis() * @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis() * @deprecated Use {@link #getTimeBetweenEvictionRuns()}. */ @Deprecated public long getTimeBetweenEvictionRunsMillis() { return timeBetweenEvictionRuns.toMillis(); } /** * Sets the value for the {@code blockWhenExhausted} configuration attribute * for pools created with this configuration instance. * * @param blockWhenExhausted The new setting of {@code blockWhenExhausted} * for this configuration instance * * @see GenericObjectPool#getBlockWhenExhausted() * @see GenericKeyedObjectPool#getBlockWhenExhausted() */ public void setBlockWhenExhausted(final boolean blockWhenExhausted) { this.blockWhenExhausted = blockWhenExhausted; } /** * Sets the value for the {@code evictionPolicyClass} configuration * attribute for pools created with this configuration instance. * * @param evictionPolicy The new setting of * {@code evictionPolicyClass} for this configuration instance * * @see GenericObjectPool#getEvictionPolicy() * @see GenericKeyedObjectPool#getEvictionPolicy() * @since 2.6.0 */ public void setEvictionPolicy(final EvictionPolicy evictionPolicy) { this.evictionPolicy = evictionPolicy; } /** * Sets the value for the {@code evictionPolicyClassName} configuration * attribute for pools created with this configuration instance. * * @param evictionPolicyClassName The new setting of * {@code evictionPolicyClassName} for this configuration instance * * @see GenericObjectPool#getEvictionPolicyClassName() * @see GenericKeyedObjectPool#getEvictionPolicyClassName() */ public void setEvictionPolicyClassName(final String evictionPolicyClassName) { this.evictionPolicyClassName = evictionPolicyClassName; } /** * Sets the value for the {@code evictorShutdownTimeout} configuration * attribute for pools created with this configuration instance. * * @param evictorShutdownTimeoutMillis The new setting of * {@code evictorShutdownTimeout} for this configuration * instance * * @see GenericObjectPool#getEvictorShutdownTimeout() * @see GenericKeyedObjectPool#getEvictorShutdownTimeout() * @since 2.10.0 */ public void setEvictorShutdownTimeoutMillis(final Duration evictorShutdownTimeoutMillis) { this.evictorShutdownTimeout = evictorShutdownTimeoutMillis; } /** * Sets the value for the {@code evictorShutdownTimeout} configuration * attribute for pools created with this configuration instance. * * @param evictorShutdownTimeoutMillis The new setting of * {@code evictorShutdownTimeout} for this configuration * instance * * @see GenericObjectPool#getEvictorShutdownTimeoutMillis() * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis() * @deprecated Use {@link #setEvictorShutdownTimeoutMillis(Duration)}. */ @Deprecated public void setEvictorShutdownTimeoutMillis(final long evictorShutdownTimeoutMillis) { this.evictorShutdownTimeout = Duration.ofMillis(evictorShutdownTimeoutMillis); } /** * Sets the value for the {@code fairness} configuration attribute for pools * created with this configuration instance. * * @param fairness The new setting of {@code fairness} * for this configuration instance * * @see GenericObjectPool#getFairness() * @see GenericKeyedObjectPool#getFairness() */ public void setFairness(final boolean fairness) { this.fairness = fairness; } /** * Sets the value of the flag that determines if JMX will be enabled for * pools created with this configuration instance. * * @param jmxEnabled The new setting of {@code jmxEnabled} * for this configuration instance */ public void setJmxEnabled(final boolean jmxEnabled) { this.jmxEnabled = jmxEnabled; } /** * Sets the value of the JMX name base that will be used as part of the * name assigned to JMX enabled pools created with this configuration * instance. A value of {@code null} means that the pool will define * the JMX name base. * * @param jmxNameBase The new setting of {@code jmxNameBase} * for this configuration instance */ public void setJmxNameBase(final String jmxNameBase) { this.jmxNameBase = jmxNameBase; } /** * Sets the value of the JMX name prefix that will be used as part of the * name assigned to JMX enabled pools created with this configuration * instance. * * @param jmxNamePrefix The new setting of {@code jmxNamePrefix} * for this configuration instance */ public void setJmxNamePrefix(final String jmxNamePrefix) { this.jmxNamePrefix = jmxNamePrefix; } /** * Sets the value for the {@code lifo} configuration attribute for pools * created with this configuration instance. * * @param lifo The new setting of {@code lifo} * for this configuration instance * * @see GenericObjectPool#getLifo() * @see GenericKeyedObjectPool#getLifo() */ public void setLifo(final boolean lifo) { this.lifo = lifo; } /** * Sets the value for the {@code maxWait} configuration attribute for pools * created with this configuration instance. * * @param maxWaitMillis The new setting of {@code maxWaitMillis} * for this configuration instance * * @see GenericObjectPool#getMaxWaitMillis() * @see GenericKeyedObjectPool#getMaxWaitMillis() */ public void setMaxWaitMillis(final long maxWaitMillis) { this.maxWaitMillis = Duration.ofMillis(maxWaitMillis); } /** * Sets the value for the {@code minEvictableIdleTime} configuration * attribute for pools created with this configuration instance. * * @param minEvictableIdleTime The new setting of * {@code minEvictableIdleTime} for this configuration instance * * @see GenericObjectPool#getMinEvictableIdleTime() * @see GenericKeyedObjectPool#getMinEvictableIdleTime() * @since 2.10.0 */ public void setMinEvictableIdleTime(final Duration minEvictableIdleTime) { this.minEvictableIdleTime = minEvictableIdleTime; } /** * Sets the value for the {@code minEvictableIdleTime} configuration * attribute for pools created with this configuration instance. * * @param minEvictableIdleTimeMillis The new setting of * {@code minEvictableIdleTime} for this configuration instance * * @see GenericObjectPool#getMinEvictableIdleTimeMillis() * @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis() * @deprecated Use {@link #setSoftMinEvictableIdleTime(Duration)}. */ @Deprecated public void setMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) { this.minEvictableIdleTime = Duration.ofMillis(minEvictableIdleTimeMillis); } /** * Sets the value for the {@code numTestsPerEvictionRun} configuration * attribute for pools created with this configuration instance. * * @param numTestsPerEvictionRun The new setting of * {@code numTestsPerEvictionRun} for this configuration instance * * @see GenericObjectPool#getNumTestsPerEvictionRun() * @see GenericKeyedObjectPool#getNumTestsPerEvictionRun() */ public void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) { this.numTestsPerEvictionRun = numTestsPerEvictionRun; } /** * Sets the value for the {@code softMinEvictableIdleTime} * configuration attribute for pools created with this configuration * instance. * * @param softMinEvictableIdleTime The new setting of * {@code softMinEvictableIdleTime} for this configuration * instance * * @see GenericObjectPool#getSoftMinEvictableIdleTime() * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTime() * @since 2.10.0 */ public void setSoftMinEvictableIdleTime(final Duration softMinEvictableIdleTime) { this.softMinEvictableIdleTime = softMinEvictableIdleTime; } /** * Sets the value for the {@code softMinEvictableIdleTime} * configuration attribute for pools created with this configuration * instance. * * @param softMinEvictableIdleTimeMillis The new setting of * {@code softMinEvictableIdleTime} for this configuration * instance * * @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis() * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis() * @deprecated Use {@link #setSoftMinEvictableIdleTime(Duration)}. */ @Deprecated public void setSoftMinEvictableIdleTimeMillis( final long softMinEvictableIdleTimeMillis) { this.softMinEvictableIdleTime = Duration.ofMillis(softMinEvictableIdleTimeMillis); } /** * Sets the value for the {@code testOnBorrow} configuration attribute for * pools created with this configuration instance. * * @param testOnBorrow The new setting of {@code testOnBorrow} * for this configuration instance * * @see GenericObjectPool#getTestOnBorrow() * @see GenericKeyedObjectPool#getTestOnBorrow() */ public void setTestOnBorrow(final boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; } /** * Sets the value for the {@code testOnCreate} configuration attribute for * pools created with this configuration instance. * * @param testOnCreate The new setting of {@code testOnCreate} * for this configuration instance * * @see GenericObjectPool#getTestOnCreate() * @see GenericKeyedObjectPool#getTestOnCreate() * * @since 2.2 */ public void setTestOnCreate(final boolean testOnCreate) { this.testOnCreate = testOnCreate; } /** * Sets the value for the {@code testOnReturn} configuration attribute for * pools created with this configuration instance. * * @param testOnReturn The new setting of {@code testOnReturn} * for this configuration instance * * @see GenericObjectPool#getTestOnReturn() * @see GenericKeyedObjectPool#getTestOnReturn() */ public void setTestOnReturn(final boolean testOnReturn) { this.testOnReturn = testOnReturn; } /** * Sets the value for the {@code testWhileIdle} configuration attribute for * pools created with this configuration instance. * * @param testWhileIdle The new setting of {@code testWhileIdle} * for this configuration instance * * @see GenericObjectPool#getTestWhileIdle() * @see GenericKeyedObjectPool#getTestWhileIdle() */ public void setTestWhileIdle(final boolean testWhileIdle) { this.testWhileIdle = testWhileIdle; } /** * Sets the value for the {@code timeBetweenEvictionRuns} configuration * attribute for pools created with this configuration instance. * * @param timeBetweenEvictionRunsMillis The new setting of * {@code timeBetweenEvictionRuns} for this configuration * instance * * @see GenericObjectPool#getTimeBetweenEvictionRuns() * @see GenericKeyedObjectPool#getTimeBetweenEvictionRuns() * @since 2.10.0 */ public void setTimeBetweenEvictionRuns(final Duration timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRuns = timeBetweenEvictionRunsMillis; } /** * Sets the value for the {@code timeBetweenEvictionRuns} configuration * attribute for pools created with this configuration instance. * * @param timeBetweenEvictionRunsMillis The new setting of * {@code timeBetweenEvictionRuns} for this configuration * instance * * @see GenericObjectPool#getTimeBetweenEvictionRunsMillis() * @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis() * @deprecated Use {@link #setTimeBetweenEvictionRuns(Duration)}. */ @Deprecated public void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRunsMillis); } @Override protected void toStringAppendFields(final StringBuilder builder) { builder.append("lifo="); builder.append(lifo); builder.append(", fairness="); builder.append(fairness); builder.append(", maxWaitMillis="); builder.append(maxWaitMillis); builder.append(", minEvictableIdleTime="); builder.append(minEvictableIdleTime); builder.append(", softMinEvictableIdleTime="); builder.append(softMinEvictableIdleTime); builder.append(", numTestsPerEvictionRun="); builder.append(numTestsPerEvictionRun); builder.append(", evictionPolicyClassName="); builder.append(evictionPolicyClassName); builder.append(", testOnCreate="); builder.append(testOnCreate); builder.append(", testOnBorrow="); builder.append(testOnBorrow); builder.append(", testOnReturn="); builder.append(testOnReturn); builder.append(", testWhileIdle="); builder.append(testWhileIdle); builder.append(", timeBetweenEvictionRuns="); builder.append(timeBetweenEvictionRuns); builder.append(", blockWhenExhausted="); builder.append(blockWhenExhausted); builder.append(", jmxEnabled="); builder.append(jmxEnabled); builder.append(", jmxNamePrefix="); builder.append(jmxNamePrefix); builder.append(", jmxNameBase="); builder.append(jmxNameBase); } } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/CallStack.java000066400000000000000000000042621405425132200322350ustar00rootroot00000000000000/* * 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.commons.pool2.impl; import java.io.PrintWriter; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.UsageTracking; /** * Strategy for obtaining and printing the current call stack. This is primarily useful for * {@linkplain UsageTracking usage tracking} so that different JVMs and configurations can use more efficient strategies * for obtaining the current call stack depending on metadata needs. * * @see CallStackUtils * @since 2.4.3 */ public interface CallStack { /** * Clears the current stack trace snapshot. Subsequent calls to {@link #printStackTrace(PrintWriter)} will be * no-ops until another call to {@link #fillInStackTrace()}. */ void clear(); /** * Takes a snapshot of the current call stack. Subsequent calls to {@link #printStackTrace(PrintWriter)} will print * out that stack trace until it is {@linkplain #clear() cleared}. */ void fillInStackTrace(); /** * Prints the current stack trace if available to a PrintWriter. The format is undefined and is primarily useful * for debugging issues with {@link PooledObject} usage in user code. * * @param writer a PrintWriter to write the current stack trace to if available * @return true if a stack trace was available to print or false if nothing was printed */ boolean printStackTrace(final PrintWriter writer); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/CallStackUtils.java000066400000000000000000000066261405425132200332640ustar00rootroot00000000000000/* * 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.commons.pool2.impl; import java.security.AccessControlException; /** * Utility methods for {@link CallStack}. * * @since 2.4.3 */ public final class CallStackUtils { /** * Tests whether the caller can create a security manager in the current environment. * * @return {@code true} if it is able to create a security manager in the current environment, {@code false} * otherwise. */ private static boolean canCreateSecurityManager() { final SecurityManager manager = System.getSecurityManager(); if (manager == null) { return true; } try { manager.checkPermission(new RuntimePermission("createSecurityManager")); return true; } catch (final AccessControlException ignored) { return false; } } /** * Constructs a new {@link CallStack} using the fastest allowed strategy. * * @param messageFormat message (or format) to print first in stack traces * @param useTimestamp if true, interpret message as a SimpleDateFormat and print the created timestamp; otherwise, * print message format literally * @return a new CallStack * @deprecated use {@link #newCallStack(String, boolean, boolean)} */ @Deprecated public static CallStack newCallStack(final String messageFormat, final boolean useTimestamp) { return newCallStack(messageFormat, useTimestamp, false); } /** * Constructs a new {@link CallStack} using the fasted allowed strategy. * * @param messageFormat message (or format) to print first in stack traces * @param useTimestamp if true, interpret message as a SimpleDateFormat and print the created timestamp; * otherwise, print message format literally * @param requireFullStackTrace if true, forces the use of a stack walking mechanism that includes full stack trace * information; otherwise, uses a faster implementation if possible * @return a new CallStack * @since 2.5 */ public static CallStack newCallStack(final String messageFormat, final boolean useTimestamp, final boolean requireFullStackTrace) { return canCreateSecurityManager() && !requireFullStackTrace ? new SecurityManagerCallStack(messageFormat, useTimestamp) : new ThrowableCallStack(messageFormat, useTimestamp); } /** * Hidden constructor. */ private CallStackUtils() { } } DefaultEvictionPolicy.java000066400000000000000000000041461405425132200345630ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import org.apache.commons.pool2.PooledObject; /** * Provides the default implementation of {@link EvictionPolicy} used by the * pools. Objects will be evicted if the following conditions are met: *
    *
  • the object has been idle longer than * {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} / * {@link GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()}
  • *
  • there are more than {@link GenericObjectPool#getMinIdle()} / * {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} idle objects in * the pool and the object has been idle for longer than * {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} / * {@link GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis()} *
*

* This class is immutable and thread-safe. *

* * @param the type of objects in the pool * * @since 2.0 */ public class DefaultEvictionPolicy implements EvictionPolicy { @Override public boolean evict(final EvictionConfig config, final PooledObject underTest, final int idleCount) { return (config.getIdleSoftEvictTime() < underTest.getIdleTimeMillis() && config.getMinIdle() < idleCount) || config.getIdleEvictTime() < underTest.getIdleTimeMillis(); } } DefaultPooledObject.java000066400000000000000000000231131405425132200341670ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.io.PrintWriter; import java.util.Deque; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectState; import org.apache.commons.pool2.TrackedUse; /** * This wrapper is used to track the additional information, such as state, for * the pooled objects. *

* This class is intended to be thread-safe. *

* * @param the type of object in the pool * * @since 2.0 */ public class DefaultPooledObject implements PooledObject { private final T object; private PooledObjectState state = PooledObjectState.IDLE; // @GuardedBy("this") to ensure transitions are valid private final long createTimeMillis = System.currentTimeMillis(); private volatile long lastBorrowTimeMillis = createTimeMillis; private volatile long lastUseTimeMillis = createTimeMillis; private volatile long lastReturnTimeMillis = createTimeMillis; private volatile boolean logAbandoned = false; private volatile CallStack borrowedBy = NoOpCallStack.INSTANCE; private volatile CallStack usedBy = NoOpCallStack.INSTANCE; private volatile long borrowedCount; /** * Creates a new instance that wraps the provided object so that the pool can * track the state of the pooled object. * * @param object The object to wrap */ public DefaultPooledObject(final T object) { this.object = object; } /** * Allocates the object. * * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} */ @Override public synchronized boolean allocate() { if (state == PooledObjectState.IDLE) { state = PooledObjectState.ALLOCATED; lastBorrowTimeMillis = System.currentTimeMillis(); lastUseTimeMillis = lastBorrowTimeMillis; borrowedCount++; if (logAbandoned) { borrowedBy.fillInStackTrace(); } return true; } if (state == PooledObjectState.EVICTION) { // TODO Allocate anyway and ignore eviction test state = PooledObjectState.EVICTION_RETURN_TO_HEAD; } // TODO if validating and testOnBorrow == true then pre-allocate for // performance return false; } @Override public int compareTo(final PooledObject other) { final long lastActiveDiff = this.getLastReturnTime() - other.getLastReturnTime(); if (lastActiveDiff == 0) { // Make sure the natural ordering is broadly consistent with equals // although this will break down if distinct objects have the same // identity hash code. // see java.lang.Comparable Javadocs return System.identityHashCode(this) - System.identityHashCode(other); } // handle int overflow return (int)Math.min(Math.max(lastActiveDiff, Integer.MIN_VALUE), Integer.MAX_VALUE); } /** * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE} * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED} * or {@link PooledObjectState#RETURNING RETURNING}. * * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED} * or {@link PooledObjectState#RETURNING RETURNING}. */ @Override public synchronized boolean deallocate() { if (state == PooledObjectState.ALLOCATED || state == PooledObjectState.RETURNING) { state = PooledObjectState.IDLE; lastReturnTimeMillis = System.currentTimeMillis(); borrowedBy.clear(); return true; } return false; } @Override public synchronized boolean endEvictionTest( final Deque> idleQueue) { if (state == PooledObjectState.EVICTION) { state = PooledObjectState.IDLE; return true; } if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) { state = PooledObjectState.IDLE; if (!idleQueue.offerFirst(this)) { // TODO - Should never happen } } return false; } @Override public long getActiveTimeMillis() { // Take copies to avoid threading issues final long rTime = lastReturnTimeMillis; final long bTime = lastBorrowTimeMillis; if (rTime > bTime) { return rTime - bTime; } return System.currentTimeMillis() - bTime; } /** * Gets the number of times this object has been borrowed. * @return The number of times this object has been borrowed. * @since 2.1 */ @Override public long getBorrowedCount() { return borrowedCount; } @Override public long getCreateTime() { return createTimeMillis; } @Override public long getIdleTimeMillis() { final long elapsed = System.currentTimeMillis() - lastReturnTimeMillis; // elapsed may be negative if: // - another thread updates lastReturnTime during the calculation window // - System.currentTimeMillis() is not monotonic (e.g. system time is set back) return elapsed >= 0 ? elapsed : 0; } @Override public long getLastBorrowTime() { return lastBorrowTimeMillis; } @Override public long getLastReturnTime() { return lastReturnTimeMillis; } /** * Gets an estimate of the last time this object was used. If the class * of the pooled object implements {@link TrackedUse}, what is returned is * the maximum of {@link TrackedUse#getLastUsed()} and * {@link #getLastBorrowTime()}; otherwise this method gives the same * value as {@link #getLastBorrowTime()}. * * @return the last time this object was used */ @Override public long getLastUsedTime() { if (object instanceof TrackedUse) { return Math.max(((TrackedUse) object).getLastUsed(), lastUseTimeMillis); } return lastUseTimeMillis; } @Override public T getObject() { return object; } /** * Gets the state of this object. * @return state */ @Override public synchronized PooledObjectState getState() { return state; } /** * Sets the state to {@link PooledObjectState#INVALID INVALID} */ @Override public synchronized void invalidate() { state = PooledObjectState.INVALID; } /** * Marks the pooled object as abandoned. */ @Override public synchronized void markAbandoned() { state = PooledObjectState.ABANDONED; } /** * Marks the object as returning to the pool. */ @Override public synchronized void markReturning() { state = PooledObjectState.RETURNING; } @Override public void printStackTrace(final PrintWriter writer) { boolean written = borrowedBy.printStackTrace(writer); written |= usedBy.printStackTrace(writer); if (written) { writer.flush(); } } @Override public void setLogAbandoned(final boolean logAbandoned) { this.logAbandoned = logAbandoned; } /** * Configures the stack trace generation strategy based on whether or not fully * detailed stack traces are required. When set to false, abandoned logs may * only include caller class information rather than method names, line numbers, * and other normal metadata available in a full stack trace. * * @param requireFullStackTrace the new configuration setting for abandoned object * logging * @since 2.5 */ @Override public void setRequireFullStackTrace(final boolean requireFullStackTrace) { borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " + "yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'", true, requireFullStackTrace); usedBy = CallStackUtils.newCallStack("The last code to use this object was:", false, requireFullStackTrace); } @Override public synchronized boolean startEvictionTest() { if (state == PooledObjectState.IDLE) { state = PooledObjectState.EVICTION; return true; } return false; } @Override public String toString() { final StringBuilder result = new StringBuilder(); result.append("Object: "); result.append(object.toString()); result.append(", State: "); synchronized (this) { result.append(state.toString()); } return result.toString(); // TODO add other attributes } @Override public void use() { lastUseTimeMillis = System.currentTimeMillis(); usedBy.fillInStackTrace(); } } DefaultPooledObjectInfo.java000066400000000000000000000064611405425132200350120ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.io.PrintWriter; import java.io.StringWriter; import java.text.SimpleDateFormat; import org.apache.commons.pool2.PooledObject; /** * Implementation of object that is used to provide information on pooled * objects via JMX. * * @since 2.0 */ public class DefaultPooledObjectInfo implements DefaultPooledObjectInfoMBean { private final PooledObject pooledObject; /** * Create a new instance for the given pooled object. * * @param pooledObject The pooled object that this instance will represent */ public DefaultPooledObjectInfo(final PooledObject pooledObject) { this.pooledObject = pooledObject; } @Override public long getBorrowedCount() { return pooledObject.getBorrowedCount(); } @Override public long getCreateTime() { return pooledObject.getCreateTime(); } @Override public String getCreateTimeFormatted() { final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); return sdf.format(Long.valueOf(pooledObject.getCreateTime())); } @Override public long getLastBorrowTime() { return pooledObject.getLastBorrowTime(); } @Override public String getLastBorrowTimeFormatted() { final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); return sdf.format(Long.valueOf(pooledObject.getLastBorrowTime())); } @Override public String getLastBorrowTrace() { final StringWriter sw = new StringWriter(); pooledObject.printStackTrace(new PrintWriter(sw)); return sw.toString(); } @Override public long getLastReturnTime() { return pooledObject.getLastReturnTime(); } @Override public String getLastReturnTimeFormatted() { final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); return sdf.format(Long.valueOf(pooledObject.getLastReturnTime())); } @Override public String getPooledObjectToString() { return pooledObject.getObject().toString(); } @Override public String getPooledObjectType() { return pooledObject.getObject().getClass().getName(); } /** * @since 2.4.3 */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("DefaultPooledObjectInfo [pooledObject="); builder.append(pooledObject); builder.append("]"); return builder.toString(); } } DefaultPooledObjectInfoMBean.java000066400000000000000000000073021405425132200357100ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; /** * The interface that defines the information about pooled objects that will be * exposed via JMX. * * NOTE: This interface exists only to define those attributes and methods that * will be made available via JMX. It must not be implemented by clients * as it is subject to change between major, minor and patch version * releases of commons pool. Clients that implement this interface may * not, therefore, be able to upgrade to a new minor or patch release * without requiring code changes. * * @since 2.0 */ public interface DefaultPooledObjectInfoMBean { /** * Get the number of times this object has been borrowed. * @return The number of times this object has been borrowed. * @since 2.1 */ long getBorrowedCount(); /** * Obtain the time (using the same basis as * {@link System#currentTimeMillis()}) that pooled object was created. * * @return The creation time for the pooled object */ long getCreateTime(); /** * Obtain the time that pooled object was created. * * @return The creation time for the pooled object formatted as * {@code yyyy-MM-dd HH:mm:ss Z} */ String getCreateTimeFormatted(); /** * Obtain the time (using the same basis as * {@link System#currentTimeMillis()}) the polled object was last borrowed. * * @return The time the pooled object was last borrowed */ long getLastBorrowTime(); /** * Obtain the time that pooled object was last borrowed. * * @return The last borrowed time for the pooled object formatted as * {@code yyyy-MM-dd HH:mm:ss Z} */ String getLastBorrowTimeFormatted(); /** * Obtain the stack trace recorded when the pooled object was last borrowed. * * @return The stack trace showing which code last borrowed the pooled * object */ String getLastBorrowTrace(); /** * Obtain the time (using the same basis as * {@link System#currentTimeMillis()})the wrapped object was last returned. * * @return The time the object was last returned */ long getLastReturnTime(); /** * Obtain the time that pooled object was last returned. * * @return The last returned time for the pooled object formatted as * {@code yyyy-MM-dd HH:mm:ss Z} */ String getLastReturnTimeFormatted(); /** * Provides a String form of the wrapper for debug purposes. The format is * not fixed and may change at any time. * * @return A string representation of the pooled object * * @see Object#toString() */ String getPooledObjectToString(); /** * Obtain the name of the class of the pooled object. * * @return The pooled object's class name * * @see Class#getName() */ String getPooledObjectType(); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/EvictionConfig.java000066400000000000000000000133271405425132200333040ustar00rootroot00000000000000/* * 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.commons.pool2.impl; import java.time.Duration; /** * This class is used by pool implementations to pass configuration information * to {@link EvictionPolicy} instances. The {@link EvictionPolicy} may also have * its own specific configuration attributes. *

* This class is immutable and thread-safe. *

* * @since 2.0 */ public class EvictionConfig { private static final Duration MAX_MILLIS_DURATION = Duration.ofMillis(Long.MAX_VALUE); private final Duration idleEvictTime; private final Duration idleSoftEvictTime; private final int minIdle; /** * Creates a new eviction configuration with the specified parameters. * Instances are immutable. * * @param poolIdleEvictTime Expected to be provided by * {@link BaseGenericObjectPool#getMinEvictableIdleTimeMillis()} * @param poolIdleSoftEvictTime Expected to be provided by * {@link BaseGenericObjectPool#getSoftMinEvictableIdleTimeMillis()} * @param minIdle Expected to be provided by * {@link GenericObjectPool#getMinIdle()} or * {@link GenericKeyedObjectPool#getMinIdlePerKey()} * @since 2.10.0 */ public EvictionConfig(final Duration poolIdleEvictTime, final Duration poolIdleSoftEvictTime, final int minIdle) { if (PoolImplUtils.isPositive(poolIdleEvictTime)) { idleEvictTime = poolIdleEvictTime; } else { idleEvictTime = MAX_MILLIS_DURATION; } if (PoolImplUtils.isPositive(poolIdleSoftEvictTime)) { idleSoftEvictTime = poolIdleSoftEvictTime; } else { idleSoftEvictTime = MAX_MILLIS_DURATION; } this.minIdle = minIdle; } /** * Creates a new eviction configuration with the specified parameters. * Instances are immutable. * * @param poolIdleEvictTime Expected to be provided by * {@link BaseGenericObjectPool#getMinEvictableIdleTimeMillis()} * @param poolIdleSoftEvictTime Expected to be provided by * {@link BaseGenericObjectPool#getSoftMinEvictableIdleTimeMillis()} * @param minIdle Expected to be provided by * {@link GenericObjectPool#getMinIdle()} or * {@link GenericKeyedObjectPool#getMinIdlePerKey()} * @deprecated Use {@link #EvictionConfig(Duration, Duration, int)}. */ @Deprecated public EvictionConfig(final long poolIdleEvictTime, final long poolIdleSoftEvictTime, final int minIdle) { this(Duration.ofMillis(poolIdleEvictTime), Duration.ofMillis(poolIdleSoftEvictTime), minIdle); } /** * Gets the {@code idleEvictTime} for this eviction configuration * instance. *

* How the evictor behaves based on this value will be determined by the * configured {@link EvictionPolicy}. *

* * @return The {@code idleEvictTime} in milliseconds */ public long getIdleEvictTime() { return idleEvictTime.toMillis(); } /** * Gets the {@code idleEvictTime} for this eviction configuration * instance. *

* How the evictor behaves based on this value will be determined by the * configured {@link EvictionPolicy}. *

* * @return The {@code idleEvictTime}. * @since 2.10.0 */ public Duration getIdleEvictTimeDuration() { return idleEvictTime; } /** * Gets the {@code idleSoftEvictTime} for this eviction configuration * instance. *

* How the evictor behaves based on this value will be determined by the * configured {@link EvictionPolicy}. *

* * @return The (@code idleSoftEvictTime} in milliseconds */ public long getIdleSoftEvictTime() { return idleSoftEvictTime.toMillis(); } /** * Gets the {@code idleSoftEvictTime} for this eviction configuration * instance. *

* How the evictor behaves based on this value will be determined by the * configured {@link EvictionPolicy}. *

* * @return The (@code idleSoftEvictTime} in milliseconds */ public Duration getIdleSoftEvictTimeDuration() { return idleSoftEvictTime; } /** * Gets the {@code minIdle} for this eviction configuration instance. *

* How the evictor behaves based on this value will be determined by the * configured {@link EvictionPolicy}. *

* * @return The {@code minIdle} */ public int getMinIdle() { return minIdle; } /** * @since 2.4 */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("EvictionConfig [idleEvictTime="); builder.append(idleEvictTime); builder.append(", idleSoftEvictTime="); builder.append(idleSoftEvictTime); builder.append(", minIdle="); builder.append(minIdle); builder.append("]"); return builder.toString(); } } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/EvictionPolicy.java000066400000000000000000000034131405425132200333310ustar00rootroot00000000000000/* * 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.commons.pool2.impl; import org.apache.commons.pool2.PooledObject; /** * To provide a custom eviction policy (i.e. something other than {@link * DefaultEvictionPolicy} for a pool, users must provide an implementation of * this interface that provides the required eviction policy. * * @param the type of objects in the pool * * @since 2.0 */ public interface EvictionPolicy { /** * This method is called to test if an idle object in the pool should be * evicted or not. * * @param config The pool configuration settings related to eviction * @param underTest The pooled object being tested for eviction * @param idleCount The current number of idle objects in the pool including * the object under test * @return {@code true} if the object should be evicted, otherwise * {@code false} */ boolean evict(EvictionConfig config, PooledObject underTest, int idleCount); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/EvictionTimer.java000066400000000000000000000202431405425132200331520ustar00rootroot00000000000000/* * 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.commons.pool2.impl; import java.lang.ref.WeakReference; import java.security.AccessController; import java.security.PrivilegedAction; import java.time.Duration; import java.util.HashMap; import java.util.Map.Entry; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; /** * Provides a shared idle object eviction timer for all pools. *

* This class is currently implemented using {@link ScheduledThreadPoolExecutor}. This implementation may change in any * future release. This class keeps track of how many pools are using it. If no pools are using the timer, it is * cancelled. This prevents a thread being left running which, in application server environments, can lead to memory * leads and/or prevent applications from shutting down or reloading cleanly. *

*

* This class has package scope to prevent its inclusion in the pool public API. The class declaration below should * *not* be changed to public. *

*

* This class is intended to be thread-safe. *

* * @since 2.0 */ class EvictionTimer { /** * Thread factory that creates a daemon thread, with the context class loader from this class. */ private static class EvictorThreadFactory implements ThreadFactory { @Override public Thread newThread(final Runnable runnable) { final Thread thread = new Thread(null, runnable, "commons-pool-evictor"); thread.setDaemon(true); // POOL-363 - Required for applications using Runtime.addShutdownHook(). AccessController.doPrivileged((PrivilegedAction) () -> { thread.setContextClassLoader(EvictorThreadFactory.class.getClassLoader()); return null; }); return thread; } } /** * Task that removes references to abandoned tasks and shuts * down the executor if there are no live tasks left. */ private static class Reaper implements Runnable { @Override public void run() { synchronized (EvictionTimer.class) { for (final Entry, WeakRunner> entry : taskMap.entrySet()) { if (entry.getKey().get() == null) { executor.remove(entry.getValue()); taskMap.remove(entry.getKey()); } } if (taskMap.isEmpty() && executor != null) { executor.shutdown(); executor.setCorePoolSize(0); executor = null; } } } } /** * Runnable that runs the referent of a weak reference. When the referent is no * no longer reachable, run is no-op. */ private static class WeakRunner implements Runnable { private final WeakReference ref; /** * Constructs a new instance to track the given reference. * * @param ref the reference to track. */ private WeakRunner(final WeakReference ref) { this.ref = ref; } @Override public void run() { final Runnable task = ref.get(); if (task != null) { task.run(); } else { executor.remove(this); taskMap.remove(ref); } } } /** Executor instance */ private static ScheduledThreadPoolExecutor executor; //@GuardedBy("EvictionTimer.class") /** Keys are weak references to tasks, values are runners managed by executor. */ private static final HashMap, WeakRunner> taskMap = new HashMap<>(); // @GuardedBy("EvictionTimer.class") /** * Removes the specified eviction task from the timer. * * @param evictor Task to be cancelled. * @param timeout If the associated executor is no longer required, how * long should this thread wait for the executor to * terminate? * @param restarting The state of the evictor. */ static synchronized void cancel(final BaseGenericObjectPool.Evictor evictor, final Duration timeout, final boolean restarting) { if (evictor != null) { evictor.cancel(); remove(evictor); } if (!restarting && executor != null && taskMap.isEmpty()) { executor.shutdown(); try { executor.awaitTermination(timeout.toMillis(), TimeUnit.MILLISECONDS); } catch (final InterruptedException e) { // Swallow // Significant API changes would be required to propagate this } executor.setCorePoolSize(0); executor = null; } } /** * @return the number of eviction tasks under management. */ static synchronized int getNumTasks() { return taskMap.size(); } /** * Removes evictor from the task set and executor. * Only called when holding the class lock. * * @param evictor Eviction task to remove */ private static void remove(final BaseGenericObjectPool.Evictor evictor) { for (final Entry, WeakRunner> entry : taskMap.entrySet()) { if (entry.getKey().get() == evictor) { executor.remove(entry.getValue()); taskMap.remove(entry.getKey()); break; } } } /** * Adds the specified eviction task to the timer. Tasks that are added with * a call to this method *must* call {@link * #cancel(BaseGenericObjectPool.Evictor, Duration, boolean)} * to cancel the task to prevent memory and/or thread leaks in application * server environments. * * @param task Task to be scheduled. * @param delay Delay in milliseconds before task is executed. * @param period Time in milliseconds between executions. */ static synchronized void schedule( final BaseGenericObjectPool.Evictor task, final Duration delay, final Duration period) { if (null == executor) { executor = new ScheduledThreadPoolExecutor(1, new EvictorThreadFactory()); executor.setRemoveOnCancelPolicy(true); executor.scheduleAtFixedRate(new Reaper(), delay.toMillis(), period.toMillis(), TimeUnit.MILLISECONDS); } final WeakReference ref = new WeakReference<>(task); final WeakRunner runner = new WeakRunner(ref); final ScheduledFuture scheduledFuture = executor.scheduleWithFixedDelay(runner, delay.toMillis(), period.toMillis(), TimeUnit.MILLISECONDS); task.setScheduledFuture(scheduledFuture); taskMap.put(ref, runner); } /** Prevents instantiation */ private EvictionTimer() { // Hide the default constructor } /** * @since 2.4.3 */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("EvictionTimer []"); return builder.toString(); } } GenericKeyedObjectPool.java000066400000000000000000002155021405425132200346350ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.time.Duration; import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Objects; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.pool2.DestroyMode; import org.apache.commons.pool2.KeyedObjectPool; import org.apache.commons.pool2.KeyedPooledObjectFactory; import org.apache.commons.pool2.PoolUtils; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectState; import org.apache.commons.pool2.SwallowedExceptionListener; import org.apache.commons.pool2.UsageTracking; /** * A configurable {@code KeyedObjectPool} implementation. *

* When coupled with the appropriate {@link KeyedPooledObjectFactory}, * {@code GenericKeyedObjectPool} provides robust pooling functionality for * keyed objects. A {@code GenericKeyedObjectPool} can be viewed as a map * of sub-pools, keyed on the (unique) key values provided to the * {@link #preparePool preparePool}, {@link #addObject addObject} or * {@link #borrowObject borrowObject} methods. Each time a new key value is * provided to one of these methods, a sub-new pool is created under the given * key to be managed by the containing {@code GenericKeyedObjectPool.} *

*

* Note that the current implementation uses a ConcurrentHashMap which uses * equals() to compare keys. * This means that distinct instance keys must be distinguishable using equals. *

*

* Optionally, one may configure the pool to examine and possibly evict objects * as they sit idle in the pool and to ensure that a minimum number of idle * objects is maintained for each key. This is performed by an "idle object * eviction" thread, which runs asynchronously. Caution should be used when * configuring this optional feature. Eviction runs contend with client threads * for access to objects in the pool, so if they run too frequently performance * issues may result. *

*

* Implementation note: To prevent possible deadlocks, care has been taken to * ensure that no call to a factory method will occur within a synchronization * block. See POOL-125 and DBCP-44 for more information. *

*

* This class is intended to be thread-safe. *

* * @see GenericObjectPool * * @param The type of keys maintained by this pool. * @param Type of element pooled in this pool. * * @since 2.0 */ public class GenericKeyedObjectPool extends BaseGenericObjectPool implements KeyedObjectPool, GenericKeyedObjectPoolMXBean, UsageTracking { /** * Maintains information on the per key queue for a given key. * * @param type of objects in the pool */ private class ObjectDeque { private final LinkedBlockingDeque> idleObjects; /* * Number of instances created - number destroyed. * Invariant: createCount <= maxTotalPerKey */ private final AtomicInteger createCount = new AtomicInteger(0); private long makeObjectCount = 0; private final Object makeObjectCountLock = new Object(); /* * The map is keyed on pooled instances, wrapped to ensure that * they work properly as keys. */ private final Map, PooledObject> allObjects = new ConcurrentHashMap<>(); /* * Number of threads with registered interest in this key. * register(K) increments this counter and deRegister(K) decrements it. * Invariant: empty keyed pool will not be dropped unless numInterested * is 0. */ private final AtomicLong numInterested = new AtomicLong(0); /** * Create a new ObjecDeque with the given fairness policy. * @param fairness true means client threads waiting to borrow / return instances * will be served as if waiting in a FIFO queue. */ public ObjectDeque(final boolean fairness) { idleObjects = new LinkedBlockingDeque<>(fairness); } /** * Obtain all the objects for the current key. * * @return All the objects */ public Map, PooledObject> getAllObjects() { return allObjects; } /** * Obtain the count of the number of objects created for the current * key. * * @return The number of objects created for this key */ public AtomicInteger getCreateCount() { return createCount; } /** * Obtain the idle objects for the current key. * * @return The idle objects */ public LinkedBlockingDeque> getIdleObjects() { return idleObjects; } /** * Obtain the number of threads with an interest registered in this key. * * @return The number of threads with a registered interest in this key */ public AtomicLong getNumInterested() { return numInterested; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("ObjectDeque [idleObjects="); builder.append(idleObjects); builder.append(", createCount="); builder.append(createCount); builder.append(", allObjects="); builder.append(allObjects); builder.append(", numInterested="); builder.append(numInterested); builder.append("]"); return builder.toString(); } } private static final Duration DEFAULT_REMOVE_ABANDONED_TIMEOUT = Duration.ofSeconds(Integer.MAX_VALUE); // JMX specific attributes private static final String ONAME_BASE = "org.apache.commons.pool2:type=GenericKeyedObjectPool,name="; //--- configuration attributes --------------------------------------------- private volatile int maxIdlePerKey = GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY; private volatile int minIdlePerKey = GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY; private volatile int maxTotalPerKey = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY; private final KeyedPooledObjectFactory factory; private final boolean fairness; /* * My hash of sub-pools (ObjectQueue). The list of keys must be kept * in step with {@link #poolKeyList} using {@link #keyLock} to ensure any * changes to the list of current keys is made in a thread-safe manner. */ private final Map> poolMap = new ConcurrentHashMap<>(); // @GuardedBy("keyLock") for write access (and some read access) /* * List of pool keys - used to control eviction order. The list of keys * must be kept in step with {@link #poolMap} using {@link #keyLock} * to ensure any changes to the list of current keys is made in a * thread-safe manner. */ private final List poolKeyList = new ArrayList<>(); // @GuardedBy("keyLock") private final ReadWriteLock keyLock = new ReentrantReadWriteLock(true); /* * The combined count of the currently active objects for all keys and those * in the process of being created. Under load, it may exceed * {@link #maxTotal} but there will never be more than {@link #maxTotal} * created at any one time. */ private final AtomicInteger numTotal = new AtomicInteger(0); private Iterator evictionKeyIterator = null; // @GuardedBy("evictionLock") private K evictionKey = null; // @GuardedBy("evictionLock") // Additional configuration properties for abandoned object tracking private volatile AbandonedConfig abandonedConfig; /** * Create a new {@code GenericKeyedObjectPool} using defaults from * {@link GenericKeyedObjectPoolConfig}. * @param factory the factory to be used to create entries */ public GenericKeyedObjectPool(final KeyedPooledObjectFactory factory) { this(factory, new GenericKeyedObjectPoolConfig<>()); } /** * Create a new {@code GenericKeyedObjectPool} using a specific * configuration. * * @param factory the factory to be used to create entries * @param config The configuration to use for this pool instance. The * configuration is used by value. Subsequent changes to * the configuration object will not be reflected in the * pool. */ public GenericKeyedObjectPool(final KeyedPooledObjectFactory factory, final GenericKeyedObjectPoolConfig config) { super(config, ONAME_BASE, config.getJmxNamePrefix()); if (factory == null) { jmxUnregister(); // tidy up throw new IllegalArgumentException("factory may not be null"); } this.factory = factory; this.fairness = config.getFairness(); setConfig(config); } /** * Creates a new {@code GenericKeyedObjectPool} that tracks and destroys * objects that are checked out, but never returned to the pool. * * @param factory The object factory to be used to create object instances * used by this pool * @param config The base pool configuration to use for this pool instance. * The configuration is used by value. Subsequent changes to * the configuration object will not be reflected in the * pool. * @param abandonedConfig Configuration for abandoned object identification * and removal. The configuration is used by value. * @since 2.10.0 */ public GenericKeyedObjectPool(final KeyedPooledObjectFactory factory, final GenericKeyedObjectPoolConfig config, final AbandonedConfig abandonedConfig) { this(factory, config); setAbandonedConfig(abandonedConfig); } /** * Add an object to the set of idle objects for a given key. * * @param key The key to associate with the idle object * @param p The wrapped object to add. * * @throws Exception If the associated factory fails to passivate the object */ private void addIdleObject(final K key, final PooledObject p) throws Exception { if (p != null) { factory.passivateObject(key, p); final LinkedBlockingDeque> idleObjects = poolMap.get(key).getIdleObjects(); if (getLifo()) { idleObjects.addFirst(p); } else { idleObjects.addLast(p); } } } /** * Create an object using the {@link KeyedPooledObjectFactory#makeObject * factory}, passivate it, and then place it in the idle object pool. * {@code addObject} is useful for "pre-loading" a pool with idle * objects. * * @param key the key a new instance should be added to * * @throws Exception when {@link KeyedPooledObjectFactory#makeObject} * fails. */ @Override public void addObject(final K key) throws Exception { assertOpen(); register(key); try { final PooledObject p = create(key); addIdleObject(key, p); } finally { deregister(key); } } /** * Equivalent to {@link #borrowObject(Object, long) borrowObject}(key, * {@link #getMaxWaitMillis()}). *

* {@inheritDoc} */ @Override public T borrowObject(final K key) throws Exception { return borrowObject(key, getMaxWaitMillis()); } /** * Borrows an object from the sub-pool associated with the given key using * the specified waiting time which only applies if * {@link #getBlockWhenExhausted()} is true. *

* If there is one or more idle instances available in the sub-pool * associated with the given key, then an idle instance will be selected * based on the value of {@link #getLifo()}, activated and returned. If * activation fails, or {@link #getTestOnBorrow() testOnBorrow} is set to * {@code true} and validation fails, the instance is destroyed and the * next available instance is examined. This continues until either a valid * instance is returned or there are no more idle instances available. *

* If there are no idle instances available in the sub-pool associated with * the given key, behavior depends on the {@link #getMaxTotalPerKey() * maxTotalPerKey}, {@link #getMaxTotal() maxTotal}, and (if applicable) * {@link #getBlockWhenExhausted()} and the value passed in to the * {@code borrowMaxWaitMillis} parameter. If the number of instances checked * out from the sub-pool under the given key is less than * {@code maxTotalPerKey} and the total number of instances in * circulation (under all keys) is less than {@code maxTotal}, a new * instance is created, activated and (if applicable) validated and returned * to the caller. If validation fails, a {@code NoSuchElementException} * will be thrown. *

* If the associated sub-pool is exhausted (no available idle instances and * no capacity to create new ones), this method will either block * ({@link #getBlockWhenExhausted()} is true) or throw a * {@code NoSuchElementException} * ({@link #getBlockWhenExhausted()} is false). * The length of time that this method will block when * {@link #getBlockWhenExhausted()} is true is determined by the value * passed in to the {@code borrowMaxWait} parameter. *

* When {@code maxTotal} is set to a positive value and this method is * invoked when at the limit with no idle instances available under the requested * key, an attempt is made to create room by clearing the oldest 15% of the * elements from the keyed sub-pools. *

* When the pool is exhausted, multiple calling threads may be * simultaneously blocked waiting for instances to become available. A * "fairness" algorithm has been implemented to ensure that threads receive * available instances in request arrival order. * * @param key pool key * @param borrowMaxWaitMillis The time to wait in milliseconds for an object * to become available * * @return object instance from the keyed pool * * @throws NoSuchElementException if a keyed object instance cannot be * returned because the pool is exhausted. * * @throws Exception if a keyed object instance cannot be returned due to an * error */ public T borrowObject(final K key, final long borrowMaxWaitMillis) throws Exception { assertOpen(); final AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getRemoveAbandonedOnBorrow() && (getNumIdle() < 2) && (getNumActive() > getMaxTotal() - 3)) { removeAbandoned(ac); } PooledObject p = null; // Get local copy of current config so it is consistent for entire // method execution final boolean blockWhenExhausted = getBlockWhenExhausted(); boolean create; final long waitTimeMillis = System.currentTimeMillis(); final ObjectDeque objectDeque = register(key); try { while (p == null) { create = false; p = objectDeque.getIdleObjects().pollFirst(); if (p == null) { p = create(key); if (p != null) { create = true; } } if (blockWhenExhausted) { if (p == null) { if (borrowMaxWaitMillis < 0) { p = objectDeque.getIdleObjects().takeFirst(); } else { p = objectDeque.getIdleObjects().pollFirst( borrowMaxWaitMillis, TimeUnit.MILLISECONDS); } } if (p == null) { throw new NoSuchElementException( "Timeout waiting for idle object"); } } else if (p == null) { throw new NoSuchElementException("Pool exhausted"); } if (!p.allocate()) { p = null; } if (p != null) { try { factory.activateObject(key, p); } catch (final Exception e) { try { destroy(key, p, true, DestroyMode.NORMAL); } catch (final Exception e1) { // Ignore - activation failure is more important } p = null; if (create) { final NoSuchElementException nsee = new NoSuchElementException( "Unable to activate object"); nsee.initCause(e); throw nsee; } } if (p != null && getTestOnBorrow()) { boolean validate = false; Throwable validationThrowable = null; try { validate = factory.validateObject(key, p); } catch (final Throwable t) { PoolUtils.checkRethrow(t); validationThrowable = t; } if (!validate) { try { destroy(key, p, true, DestroyMode.NORMAL); destroyedByBorrowValidationCount.incrementAndGet(); } catch (final Exception e) { // Ignore - validation failure is more important } p = null; if (create) { final NoSuchElementException nsee = new NoSuchElementException( "Unable to validate object"); nsee.initCause(validationThrowable); throw nsee; } } } } } } finally { deregister(key); } updateStatsBorrow(p, Duration.ofMillis(System.currentTimeMillis() - waitTimeMillis)); return p.getObject(); } /** * Calculate the number of objects that need to be created to attempt to * maintain the minimum number of idle objects while not exceeded the limits * on the maximum number of objects either per key or totally. * * @param objectDeque The set of objects to check * * @return The number of new objects to create */ private int calculateDeficit(final ObjectDeque objectDeque) { if (objectDeque == null) { return getMinIdlePerKey(); } // Used more than once so keep a local copy so the value is consistent final int maxTotal = getMaxTotal(); final int maxTotalPerKeySave = getMaxTotalPerKey(); // Calculate no of objects needed to be created, in order to have // the number of pooled objects < maxTotalPerKey(); int objectDefecit = getMinIdlePerKey() - objectDeque.getIdleObjects().size(); if (maxTotalPerKeySave > 0) { final int growLimit = Math.max(0, maxTotalPerKeySave - objectDeque.getIdleObjects().size()); objectDefecit = Math.min(objectDefecit, growLimit); } // Take the maxTotal limit into account if (maxTotal > 0) { final int growLimit = Math.max(0, maxTotal - getNumActive() - getNumIdle()); objectDefecit = Math.min(objectDefecit, growLimit); } return objectDefecit; } /** * Clears any objects sitting idle in the pool by removing them from the * idle instance sub-pools and then invoking the configured * PoolableObjectFactory's * {@link KeyedPooledObjectFactory#destroyObject(Object, PooledObject)} * method on each idle instance. *

* Implementation notes: *

    *
  • This method does not destroy or effect in any way instances that are * checked out when it is invoked.
  • *
  • Invoking this method does not prevent objects being returned to the * idle instance pool, even during its execution. Additional instances may * be returned while removed items are being destroyed.
  • *
  • Exceptions encountered destroying idle instances are swallowed * but notified via a {@link SwallowedExceptionListener}.
  • *
*/ @Override public void clear() { final Iterator iter = poolMap.keySet().iterator(); while (iter.hasNext()) { clear(iter.next()); } } /** * Clears the specified sub-pool, removing all pooled instances * corresponding to the given {@code key}. Exceptions encountered * destroying idle instances are swallowed but notified via a * {@link SwallowedExceptionListener}. * * @param key the key to clear */ @Override public void clear(final K key) { final ObjectDeque objectDeque = register(key); try { final LinkedBlockingDeque> idleObjects = objectDeque.getIdleObjects(); PooledObject p = idleObjects.poll(); while (p != null) { try { destroy(key, p, true, DestroyMode.NORMAL); } catch (final Exception e) { swallowException(e); } p = idleObjects.poll(); } } finally { deregister(key); } } /** * Clears oldest 15% of objects in pool. The method sorts the objects into * a TreeMap and then iterates the first 15% for removal. */ public void clearOldest() { // build sorted map of idle objects final Map, K> map = new TreeMap<>(); for (final Map.Entry> entry : poolMap.entrySet()) { final K k = entry.getKey(); final ObjectDeque deque = entry.getValue(); // Protect against possible NPE if key has been removed in another // thread. Not worth locking the keys while this loop completes. if (deque != null) { final LinkedBlockingDeque> idleObjects = deque.getIdleObjects(); for (final PooledObject p : idleObjects) { // each item into the map using the PooledObject object as the // key. It then gets sorted based on the idle time map.put(p, k); } } } // Now iterate created map and kill the first 15% plus one to account // for zero int itemsToRemove = ((int) (map.size() * 0.15)) + 1; final Iterator, K>> iter = map.entrySet().iterator(); while (iter.hasNext() && itemsToRemove > 0) { final Map.Entry, K> entry = iter.next(); // kind of backwards on naming. In the map, each key is the // PooledObject because it has the ordering with the timestamp // value. Each value that the key references is the key of the // list it belongs to. final K key = entry.getValue(); final PooledObject p = entry.getKey(); // Assume the destruction succeeds boolean destroyed = true; try { destroyed = destroy(key, p, false, DestroyMode.NORMAL); } catch (final Exception e) { swallowException(e); } if (destroyed) { itemsToRemove--; } } } /** * Closes the keyed object pool. Once the pool is closed, * {@link #borrowObject(Object)} will fail with IllegalStateException, but * {@link #returnObject(Object, Object)} and * {@link #invalidateObject(Object, Object)} will continue to work, with * returned objects destroyed on return. *

* Destroys idle instances in the pool by invoking {@link #clear()}. */ @Override public void close() { if (isClosed()) { return; } synchronized (closeLock) { if (isClosed()) { return; } // Stop the evictor before the pool is closed since evict() calls // assertOpen() stopEvictor(); closed = true; // This clear removes any idle objects clear(); jmxUnregister(); // Release any threads that were waiting for an object final Iterator> iter = poolMap.values().iterator(); while (iter.hasNext()) { iter.next().getIdleObjects().interuptTakeWaiters(); } // This clear cleans up the keys now any waiting threads have been // interrupted clear(); } } /** * Create a new pooled object. * * @param key Key associated with new pooled object * * @return The new, wrapped pooled object * * @throws Exception If the objection creation fails */ private PooledObject create(final K key) throws Exception { int maxTotalPerKeySave = getMaxTotalPerKey(); // Per key if (maxTotalPerKeySave < 0) { maxTotalPerKeySave = Integer.MAX_VALUE; } final int maxTotal = getMaxTotal(); // All keys final ObjectDeque objectDeque = poolMap.get(key); // Check against the overall limit boolean loop = true; while (loop) { final int newNumTotal = numTotal.incrementAndGet(); if (maxTotal > -1 && newNumTotal > maxTotal) { numTotal.decrementAndGet(); if (getNumIdle() == 0) { return null; } clearOldest(); } else { loop = false; } } // Flag that indicates if create should: // - TRUE: call the factory to create an object // - FALSE: return null // - null: loop and re-test the condition that determines whether to // call the factory Boolean create = null; while (create == null) { synchronized (objectDeque.makeObjectCountLock) { final long newCreateCount = objectDeque.getCreateCount().incrementAndGet(); // Check against the per key limit if (newCreateCount > maxTotalPerKeySave) { // The key is currently at capacity or in the process of // making enough new objects to take it to capacity. objectDeque.getCreateCount().decrementAndGet(); if (objectDeque.makeObjectCount == 0) { // There are no makeObject() calls in progress for this // key so the key is at capacity. Do not attempt to // create a new object. Return and wait for an object to // be returned. create = Boolean.FALSE; } else { // There are makeObject() calls in progress that might // bring the pool to capacity. Those calls might also // fail so wait until they complete and then re-test if // the pool is at capacity or not. objectDeque.makeObjectCountLock.wait(); } } else { // The pool is not at capacity. Create a new object. objectDeque.makeObjectCount++; create = Boolean.TRUE; } } } if (!create.booleanValue()) { numTotal.decrementAndGet(); return null; } PooledObject p = null; try { p = factory.makeObject(key); if (getTestOnCreate() && !factory.validateObject(key, p)) { numTotal.decrementAndGet(); objectDeque.getCreateCount().decrementAndGet(); return null; } } catch (final Exception e) { numTotal.decrementAndGet(); objectDeque.getCreateCount().decrementAndGet(); throw e; } finally { synchronized (objectDeque.makeObjectCountLock) { objectDeque.makeObjectCount--; objectDeque.makeObjectCountLock.notifyAll(); } } final AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getLogAbandoned()) { p.setLogAbandoned(true); p.setRequireFullStackTrace(ac.getRequireFullStackTrace()); } createdCount.incrementAndGet(); objectDeque.getAllObjects().put(new IdentityWrapper<>(p.getObject()), p); return p; } /** * De-register the use of a key by an object. *

* register() and deregister() must always be used as a pair. * * @param k The key to de-register */ private void deregister(final K k) { Lock lock = keyLock.readLock(); try { lock.lock(); final ObjectDeque objectDeque = poolMap.get(k); final long numInterested = objectDeque.getNumInterested().decrementAndGet(); if (numInterested == 0 && objectDeque.getCreateCount().get() == 0) { // Potential to remove key // Upgrade to write lock lock.unlock(); lock = keyLock.writeLock(); lock.lock(); if (objectDeque.getCreateCount().get() == 0 && objectDeque.getNumInterested().get() == 0) { // NOTE: Keys must always be removed from both poolMap and // poolKeyList at the same time while protected by // keyLock.writeLock() poolMap.remove(k); poolKeyList.remove(k); } } } finally { lock.unlock(); } } /** * Destroy the wrapped, pooled object. * * @param key The key associated with the object to destroy. * @param toDestroy The wrapped object to be destroyed * @param always Should the object be destroyed even if it is not currently * in the set of idle objects for the given key * @param mode DestroyMode context provided to the factory * * @return {@code true} if the object was destroyed, otherwise {@code false} * @throws Exception If the object destruction failed */ private boolean destroy(final K key, final PooledObject toDestroy, final boolean always, final DestroyMode mode) throws Exception { final ObjectDeque objectDeque = register(key); try { boolean isIdle; synchronized(toDestroy) { // Check idle state directly isIdle = toDestroy.getState().equals(PooledObjectState.IDLE); // If idle, not under eviction test, or always is true, remove instance, // updating isIdle if instance is found in idle objects if (isIdle || always) { isIdle = objectDeque.getIdleObjects().remove(toDestroy); } } if (isIdle || always) { objectDeque.getAllObjects().remove(new IdentityWrapper<>(toDestroy.getObject())); toDestroy.invalidate(); try { factory.destroyObject(key, toDestroy, mode); } finally { objectDeque.getCreateCount().decrementAndGet(); destroyedCount.incrementAndGet(); numTotal.decrementAndGet(); } return true; } return false; } finally { deregister(key); } } @Override void ensureMinIdle() throws Exception { final int minIdlePerKeySave = getMinIdlePerKey(); if (minIdlePerKeySave < 1) { return; } for (final K k : poolMap.keySet()) { ensureMinIdle(k); } } /** * Ensure that the configured number of minimum idle objects is available in * the pool for the given key. * * @param key The key to check for idle objects * * @throws Exception If a new object is required and cannot be created */ private void ensureMinIdle(final K key) throws Exception { // Calculate current pool objects ObjectDeque objectDeque = poolMap.get(key); // objectDeque == null is OK here. It is handled correctly by both // methods called below. // this method isn't synchronized so the // calculateDeficit is done at the beginning // as a loop limit and a second time inside the loop // to stop when another thread already returned the // needed objects final int deficit = calculateDeficit(objectDeque); for (int i = 0; i < deficit && calculateDeficit(objectDeque) > 0; i++) { addObject(key); // If objectDeque was null, it won't be any more. Obtain a reference // to it so the deficit can be correctly calculated. It needs to // take account of objects created in other threads. if (objectDeque == null) { objectDeque = poolMap.get(key); } } } /** * {@inheritDoc} *

* Successive activations of this method examine objects in keyed sub-pools * in sequence, cycling through the keys and examining objects in * oldest-to-youngest order within the keyed sub-pools. */ @Override public void evict() throws Exception { assertOpen(); if (getNumIdle() > 0) { PooledObject underTest = null; final EvictionPolicy evictionPolicy = getEvictionPolicy(); synchronized (evictionLock) { final EvictionConfig evictionConfig = new EvictionConfig( getMinEvictableIdleTime(), getSoftMinEvictableIdleTime(), getMinIdlePerKey()); final boolean testWhileIdle = getTestWhileIdle(); for (int i = 0, m = getNumTests(); i < m; i++) { if(evictionIterator == null || !evictionIterator.hasNext()) { if (evictionKeyIterator == null || !evictionKeyIterator.hasNext()) { final List keyCopy = new ArrayList<>(); final Lock readLock = keyLock.readLock(); readLock.lock(); try { keyCopy.addAll(poolKeyList); } finally { readLock.unlock(); } evictionKeyIterator = keyCopy.iterator(); } while (evictionKeyIterator.hasNext()) { evictionKey = evictionKeyIterator.next(); final ObjectDeque objectDeque = poolMap.get(evictionKey); if (objectDeque == null) { continue; } final Deque> idleObjects = objectDeque.getIdleObjects(); evictionIterator = new EvictionIterator(idleObjects); if (evictionIterator.hasNext()) { break; } evictionIterator = null; } } if (evictionIterator == null) { // Pools exhausted return; } final Deque> idleObjects; try { underTest = evictionIterator.next(); idleObjects = evictionIterator.getIdleObjects(); } catch (final NoSuchElementException nsee) { // Object was borrowed in another thread // Don't count this as an eviction test so reduce i; i--; evictionIterator = null; continue; } if (!underTest.startEvictionTest()) { // Object was borrowed in another thread // Don't count this as an eviction test so reduce i; i--; continue; } // User provided eviction policy could throw all sorts of // crazy exceptions. Protect against such an exception // killing the eviction thread. boolean evict; try { evict = evictionPolicy.evict(evictionConfig, underTest, poolMap.get(evictionKey).getIdleObjects().size()); } catch (final Throwable t) { // Slightly convoluted as SwallowedExceptionListener // uses Exception rather than Throwable PoolUtils.checkRethrow(t); swallowException(new Exception(t)); // Don't evict on error conditions evict = false; } if (evict) { destroy(evictionKey, underTest, true, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); } else { if (testWhileIdle) { boolean active = false; try { factory.activateObject(evictionKey, underTest); active = true; } catch (final Exception e) { destroy(evictionKey, underTest, true, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); } if (active) { if (!factory.validateObject(evictionKey, underTest)) { destroy(evictionKey, underTest, true, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); } else { try { factory.passivateObject(evictionKey, underTest); } catch (final Exception e) { destroy(evictionKey, underTest, true, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); } } } } if (!underTest.endEvictionTest(idleObjects)) { // TODO - May need to add code here once additional // states are used } } } } } final AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getRemoveAbandonedOnMaintenance()) { removeAbandoned(ac); } } /** * Obtain a reference to the factory used to create, destroy and validate * the objects used by this pool. * * @return the factory */ public KeyedPooledObjectFactory getFactory() { return factory; } /** * Gets whether this pool identifies and logs any abandoned objects. * * @return {@code true} if abandoned object removal is configured for this * pool and removal events are to be logged otherwise {@code false} * * @see AbandonedConfig#getLogAbandoned() * @since 2.10.0 */ @Override public boolean getLogAbandoned() { final AbandonedConfig ac = this.abandonedConfig; return ac != null && ac.getLogAbandoned(); } /** * Gets the cap on the number of "idle" instances per key in the pool. * If maxIdlePerKey is set too low on heavily loaded systems it is possible * you will see objects being destroyed and almost immediately new objects * being created. This is a result of the active threads momentarily * returning objects faster than they are requesting them, causing the * number of idle objects to rise above maxIdlePerKey. The best value for * maxIdlePerKey for heavily loaded system will vary but the default is a * good starting point. * * @return the maximum number of "idle" instances that can be held in a * given keyed sub-pool or a negative value if there is no limit * * @see #setMaxIdlePerKey */ @Override public int getMaxIdlePerKey() { return maxIdlePerKey; } /** * Gets the limit on the number of object instances allocated by the pool * (checked out or idle), per key. When the limit is reached, the sub-pool * is said to be exhausted. A negative value indicates no limit. * * @return the limit on the number of active instances per key * * @see #setMaxTotalPerKey */ @Override public int getMaxTotalPerKey() { return maxTotalPerKey; } /** * Gets the target for the minimum number of idle objects to maintain in * each of the keyed sub-pools. This setting only has an effect if it is * positive and {@link #getTimeBetweenEvictionRunsMillis()} is greater than * zero. If this is the case, an attempt is made to ensure that each * sub-pool has the required minimum number of instances during idle object * eviction runs. *

* If the configured value of minIdlePerKey is greater than the configured * value for maxIdlePerKey then the value of maxIdlePerKey will be used * instead. * * @return minimum size of the each keyed pool * * @see #setTimeBetweenEvictionRunsMillis */ @Override public int getMinIdlePerKey() { final int maxIdlePerKeySave = getMaxIdlePerKey(); if (this.minIdlePerKey > maxIdlePerKeySave) { return maxIdlePerKeySave; } return minIdlePerKey; } @Override public int getNumActive() { return numTotal.get() - getNumIdle(); } @Override public int getNumActive(final K key) { final ObjectDeque objectDeque = poolMap.get(key); if (objectDeque != null) { return objectDeque.getAllObjects().size() - objectDeque.getIdleObjects().size(); } return 0; } @Override public Map getNumActivePerKey() { final HashMap result = new HashMap<>(); final Iterator>> iter = poolMap.entrySet().iterator(); while (iter.hasNext()) { final Entry> entry = iter.next(); if (entry != null) { final K key = entry.getKey(); final ObjectDeque objectDequeue = entry.getValue(); if (key != null && objectDequeue != null) { result.put(key.toString(), Integer.valueOf( objectDequeue.getAllObjects().size() - objectDequeue.getIdleObjects().size())); } } } return result; } @Override public int getNumIdle() { final Iterator> iter = poolMap.values().iterator(); int result = 0; while (iter.hasNext()) { result += iter.next().getIdleObjects().size(); } return result; } //--- JMX support ---------------------------------------------------------- @Override public int getNumIdle(final K key) { final ObjectDeque objectDeque = poolMap.get(key); return objectDeque != null ? objectDeque.getIdleObjects().size() : 0; } /** * Calculate the number of objects to test in a run of the idle object * evictor. * * @return The number of objects to test for validity */ private int getNumTests() { final int totalIdle = getNumIdle(); final int numTests = getNumTestsPerEvictionRun(); if (numTests >= 0) { return Math.min(numTests, totalIdle); } return(int)(Math.ceil(totalIdle/Math.abs((double)numTests))); } /** * Return an estimate of the number of threads currently blocked waiting for * an object from the pool. This is intended for monitoring only, not for * synchronization control. * * @return The estimate of the number of threads currently blocked waiting * for an object from the pool */ @Override public int getNumWaiters() { int result = 0; if (getBlockWhenExhausted()) { final Iterator> iter = poolMap.values().iterator(); while (iter.hasNext()) { // Assume no overflow result += iter.next().getIdleObjects().getTakeQueueLength(); } } return result; } /** * Return an estimate of the number of threads currently blocked waiting for * an object from the pool for each key. This is intended for * monitoring only, not for synchronization control. * * @return The estimate of the number of threads currently blocked waiting * for an object from the pool for each key */ @Override public Map getNumWaitersByKey() { final Map result = new HashMap<>(); for (final Map.Entry> entry : poolMap.entrySet()) { final K k = entry.getKey(); final ObjectDeque deque = entry.getValue(); if (deque != null) { if (getBlockWhenExhausted()) { result.put(k.toString(), Integer.valueOf( deque.getIdleObjects().getTakeQueueLength())); } else { result.put(k.toString(), Integer.valueOf(0)); } } } return result; } /** * Gets whether a check is made for abandoned objects when an object is borrowed * from this pool. * * @return {@code true} if abandoned object removal is configured to be * activated by borrowObject otherwise {@code false} * * @see AbandonedConfig#getRemoveAbandonedOnBorrow() * @since 2.10.0 */ @Override public boolean getRemoveAbandonedOnBorrow() { final AbandonedConfig ac = this.abandonedConfig; return ac != null && ac.getRemoveAbandonedOnBorrow(); } /** * Gets whether a check is made for abandoned objects when the evictor runs. * * @return {@code true} if abandoned object removal is configured to be * activated when the evictor runs otherwise {@code false} * * @see AbandonedConfig#getRemoveAbandonedOnMaintenance() * @since 2.10.0 */ @Override public boolean getRemoveAbandonedOnMaintenance() { final AbandonedConfig ac = this.abandonedConfig; return ac != null && ac.getRemoveAbandonedOnMaintenance(); } /** * Gets the timeout before which an object will be considered to be * abandoned by this pool. * * @return The abandoned object timeout in seconds if abandoned object * removal is configured for this pool; Integer.MAX_VALUE otherwise. * * @see AbandonedConfig#getRemoveAbandonedTimeout() * @see AbandonedConfig#getRemoveAbandonedTimeoutDuration() * @deprecated Use {@link #getRemoveAbandonedTimeoutDuration()}. * @since 2.10.0 */ @Override @Deprecated public int getRemoveAbandonedTimeout() { final AbandonedConfig ac = this.abandonedConfig; return ac != null ? ac.getRemoveAbandonedTimeout() : Integer.MAX_VALUE; } /** * Gets the timeout before which an object will be considered to be * abandoned by this pool. * * @return The abandoned object timeout in seconds if abandoned object * removal is configured for this pool; Integer.MAX_VALUE otherwise. * * @see AbandonedConfig#getRemoveAbandonedTimeout() * @see AbandonedConfig#getRemoveAbandonedTimeoutDuration() * @since 2.10.0 */ public Duration getRemoveAbandonedTimeoutDuration() { final AbandonedConfig ac = this.abandonedConfig; return ac != null ? ac.getRemoveAbandonedTimeoutDuration() : DEFAULT_REMOVE_ABANDONED_TIMEOUT; } //--- inner classes ---------------------------------------------- /** * Checks to see if there are any threads currently waiting to borrow * objects but are blocked waiting for more objects to become available. * * @return {@code true} if there is at least one thread waiting otherwise * {@code false} */ private boolean hasBorrowWaiters() { for (final Map.Entry> entry : poolMap.entrySet()) { final ObjectDeque deque = entry.getValue(); if (deque != null) { final LinkedBlockingDeque> pool = deque.getIdleObjects(); if(pool.hasTakeWaiters()) { return true; } } } return false; } /** * {@inheritDoc} *

* Activation of this method decrements the active count associated with * the given keyed pool and attempts to destroy {@code obj.} * * @param key pool key * @param obj instance to invalidate * * @throws Exception if an exception occurs destroying the * object * @throws IllegalStateException if obj does not belong to the pool * under the given key */ @Override public void invalidateObject(final K key, final T obj) throws Exception { invalidateObject(key, obj, DestroyMode.NORMAL); } /** * {@inheritDoc} *

* Activation of this method decrements the active count associated with * the given keyed pool and attempts to destroy {@code obj.} * * @param key pool key * @param obj instance to invalidate * @param mode DestroyMode context provided to factory * * @throws Exception if an exception occurs destroying the * object * @throws IllegalStateException if obj does not belong to the pool * under the given key * @since 2.9.0 */ @Override public void invalidateObject(final K key, final T obj, final DestroyMode mode) throws Exception { final ObjectDeque objectDeque = poolMap.get(key); final PooledObject p = objectDeque.getAllObjects().get(new IdentityWrapper<>(obj)); if (p == null) { throw new IllegalStateException( "Object not currently part of this pool"); } synchronized (p) { if (p.getState() != PooledObjectState.INVALID) { destroy(key, p, true, mode); } } if (objectDeque.idleObjects.hasTakeWaiters()) { addObject(key); } } /** * Gets whether or not abandoned object removal is configured for this pool. * * @return true if this pool is configured to detect and remove * abandoned objects * @since 2.10.0 */ @Override public boolean isAbandonedConfig() { return abandonedConfig != null; } /** * Provides information on all the objects in the pool, both idle (waiting * to be borrowed) and active (currently borrowed). *

* Note: This is named listAllObjects so it is presented as an operation via * JMX. That means it won't be invoked unless the explicitly requested * whereas all attributes will be automatically requested when viewing the * attributes for an object in a tool like JConsole. * * @return Information grouped by key on all the objects in the pool */ @Override public Map> listAllObjects() { final Map> result = new HashMap<>(); for (final Map.Entry> entry : poolMap.entrySet()) { final K k = entry.getKey(); final ObjectDeque deque = entry.getValue(); if (deque != null) { final List list = new ArrayList<>(); result.put(k.toString(), list); for (final PooledObject p : deque.getAllObjects().values()) { list.add(new DefaultPooledObjectInfo(p)); } } } return result; } /** * Registers a key for pool control and ensures that * {@link #getMinIdlePerKey()} idle instances are created. * * @param key - The key to register for pool control. * * @throws Exception If the associated factory throws an exception */ public void preparePool(final K key) throws Exception { final int minIdlePerKeySave = getMinIdlePerKey(); if (minIdlePerKeySave < 1) { return; } ensureMinIdle(key); } /** * Register the use of a key by an object. *

* register() and deregister() must always be used as a pair. * * @param k The key to register * * @return The objects currently associated with the given key. If this * method returns without throwing an exception then it will never * return null. */ private ObjectDeque register(final K k) { Lock lock = keyLock.readLock(); ObjectDeque objectDeque = null; try { lock.lock(); objectDeque = poolMap.get(k); if (objectDeque == null) { // Upgrade to write lock lock.unlock(); lock = keyLock.writeLock(); lock.lock(); objectDeque = poolMap.get(k); if (objectDeque == null) { objectDeque = new ObjectDeque<>(fairness); objectDeque.getNumInterested().incrementAndGet(); // NOTE: Keys must always be added to both poolMap and // poolKeyList at the same time while protected by // keyLock.writeLock() poolMap.put(k, objectDeque); poolKeyList.add(k); } else { objectDeque.getNumInterested().incrementAndGet(); } } else { objectDeque.getNumInterested().incrementAndGet(); } } finally { lock.unlock(); } return objectDeque; } //--- internal attributes -------------------------------------------------- /** * Recovers abandoned objects which have been checked out but * not used since longer than the removeAbandonedTimeout. * * @param abandonedConfig The configuration to use to identify abandoned objects */ @SuppressWarnings("resource") // PrintWriter is managed elsewhere private void removeAbandoned(final AbandonedConfig abandonedConfig) { for (final Entry.ObjectDeque> pool : poolMap.entrySet()) { final Map, PooledObject> allObjects = pool.getValue().getAllObjects(); // Generate a list of abandoned objects to remove final long nowMillis = System.currentTimeMillis(); final long timeoutMillis = nowMillis - abandonedConfig.getRemoveAbandonedTimeoutDuration().toMillis(); final ArrayList> remove = new ArrayList<>(); final Iterator> it = allObjects.values().iterator(); while (it.hasNext()) { final PooledObject pooledObject = it.next(); synchronized (pooledObject) { if (pooledObject.getState() == PooledObjectState.ALLOCATED && pooledObject.getLastUsedTime() <= timeoutMillis) { pooledObject.markAbandoned(); remove.add(pooledObject); } } } // Now remove the abandoned objects final Iterator> itr = remove.iterator(); while (itr.hasNext()) { final PooledObject pooledObject = itr.next(); if (abandonedConfig.getLogAbandoned()) { pooledObject.printStackTrace(abandonedConfig.getLogWriter()); } try { invalidateObject(pool.getKey(), pooledObject.getObject(), DestroyMode.ABANDONED); } catch (final Exception e) { e.printStackTrace(); } } } } /** * Returns an object to a keyed sub-pool. *

* If {@link #getMaxIdlePerKey() maxIdle} is set to a positive value and the * number of idle instances under the given key has reached this value, the * returning instance is destroyed. *

* If {@link #getTestOnReturn() testOnReturn} == true, the returning * instance is validated before being returned to the idle instance sub-pool * under the given key. In this case, if validation fails, the instance is * destroyed. *

* Exceptions encountered destroying objects for any reason are swallowed * but notified via a {@link SwallowedExceptionListener}. * * @param key pool key * @param obj instance to return to the keyed pool * * @throws IllegalStateException if an object is returned to the pool that * was not borrowed from it or if an object is * returned to the pool multiple times */ @Override public void returnObject(final K key, final T obj) { final ObjectDeque objectDeque = poolMap.get(key); if (objectDeque == null) { throw new IllegalStateException( "No keyed pool found under the given key."); } final PooledObject p = objectDeque.getAllObjects().get(new IdentityWrapper<>(obj)); if (p == null) { throw new IllegalStateException( "Returned object not currently part of this pool"); } markReturningState(p); final Duration activeTime = p.getActiveTime(); try { if (getTestOnReturn() && !factory.validateObject(key, p)) { try { destroy(key, p, true, DestroyMode.NORMAL); } catch (final Exception e) { swallowException(e); } whenWaitersAddObject(key, objectDeque.idleObjects); return; } try { factory.passivateObject(key, p); } catch (final Exception e1) { swallowException(e1); try { destroy(key, p, true, DestroyMode.NORMAL); } catch (final Exception e) { swallowException(e); } whenWaitersAddObject(key, objectDeque.idleObjects); return; } if (!p.deallocate()) { throw new IllegalStateException( "Object has already been returned to this pool"); } final int maxIdle = getMaxIdlePerKey(); final LinkedBlockingDeque> idleObjects = objectDeque.getIdleObjects(); if (isClosed() || maxIdle > -1 && maxIdle <= idleObjects.size()) { try { destroy(key, p, true, DestroyMode.NORMAL); } catch (final Exception e) { swallowException(e); } } else { if (getLifo()) { idleObjects.addFirst(p); } else { idleObjects.addLast(p); } if (isClosed()) { // Pool closed while object was being added to idle objects. // Make sure the returned object is destroyed rather than left // in the idle object pool (which would effectively be a leak) clear(key); } } } finally { if (hasBorrowWaiters()) { reuseCapacity(); } updateStatsReturn(activeTime); } } /** * Attempt to create one new instance to serve from the most heavily * loaded pool that can add a new instance. * * This method exists to ensure liveness in the pool when threads are * parked waiting and capacity to create instances under the requested keys * subsequently becomes available. * * This method is not guaranteed to create an instance and its selection * of the most loaded pool that can create an instance may not always be * correct, since it does not lock the pool and instances may be created, * borrowed, returned or destroyed by other threads while it is executing. */ private void reuseCapacity() { final int maxTotalPerKeySave = getMaxTotalPerKey(); // Find the most loaded pool that could take a new instance int maxQueueLength = 0; LinkedBlockingDeque> mostLoaded = null; K loadedKey = null; for (final Map.Entry> entry : poolMap.entrySet()) { final K k = entry.getKey(); final ObjectDeque deque = entry.getValue(); if (deque != null) { final LinkedBlockingDeque> pool = deque.getIdleObjects(); final int queueLength = pool.getTakeQueueLength(); if (getNumActive(k) < maxTotalPerKeySave && queueLength > maxQueueLength) { maxQueueLength = queueLength; mostLoaded = pool; loadedKey = k; } } } // Attempt to add an instance to the most loaded pool if (mostLoaded != null) { register(loadedKey); try { final PooledObject p = create(loadedKey); if (p != null) { addIdleObject(loadedKey, p); } } catch (final Exception e) { swallowException(e); } finally { deregister(loadedKey); } } } /** * Sets the abandoned object removal configuration. * * @param abandonedConfig the new configuration to use. This is used by value. * * @see AbandonedConfig * @since 2.10.0 */ @SuppressWarnings("resource") // PrintWriter is managed elsewhere public void setAbandonedConfig(final AbandonedConfig abandonedConfig) { if (abandonedConfig == null) { this.abandonedConfig = null; } else { this.abandonedConfig = new AbandonedConfig(); this.abandonedConfig.setLogAbandoned(abandonedConfig.getLogAbandoned()); this.abandonedConfig.setLogWriter(abandonedConfig.getLogWriter()); this.abandonedConfig.setRemoveAbandonedOnBorrow(abandonedConfig.getRemoveAbandonedOnBorrow()); this.abandonedConfig.setRemoveAbandonedOnMaintenance(abandonedConfig.getRemoveAbandonedOnMaintenance()); this.abandonedConfig.setRemoveAbandonedTimeout(abandonedConfig.getRemoveAbandonedTimeoutDuration()); this.abandonedConfig.setUseUsageTracking(abandonedConfig.getUseUsageTracking()); this.abandonedConfig.setRequireFullStackTrace(abandonedConfig.getRequireFullStackTrace()); } } /** * Sets the configuration. * * @param conf the new configuration to use. This is used by value. * * @see GenericKeyedObjectPoolConfig */ public void setConfig(final GenericKeyedObjectPoolConfig conf) { super.setConfig(conf); setMaxIdlePerKey(conf.getMaxIdlePerKey()); setMaxTotalPerKey(conf.getMaxTotalPerKey()); setMaxTotal(conf.getMaxTotal()); setMinIdlePerKey(conf.getMinIdlePerKey()); } /** * Sets the cap on the number of "idle" instances per key in the pool. * If maxIdlePerKey is set too low on heavily loaded systems it is possible * you will see objects being destroyed and almost immediately new objects * being created. This is a result of the active threads momentarily * returning objects faster than they are requesting them, causing the * number of idle objects to rise above maxIdlePerKey. The best value for * maxIdlePerKey for heavily loaded system will vary but the default is a * good starting point. * * @param maxIdlePerKey the maximum number of "idle" instances that can be * held in a given keyed sub-pool. Use a negative value * for no limit * * @see #getMaxIdlePerKey */ public void setMaxIdlePerKey(final int maxIdlePerKey) { this.maxIdlePerKey = maxIdlePerKey; } /** * Sets the limit on the number of object instances allocated by the pool * (checked out or idle), per key. When the limit is reached, the sub-pool * is said to be exhausted. A negative value indicates no limit. * * @param maxTotalPerKey the limit on the number of active instances per key * * @see #getMaxTotalPerKey */ public void setMaxTotalPerKey(final int maxTotalPerKey) { this.maxTotalPerKey = maxTotalPerKey; } /** * Sets the target for the minimum number of idle objects to maintain in * each of the keyed sub-pools. This setting only has an effect if it is * positive and {@link #getTimeBetweenEvictionRunsMillis()} is greater than * zero. If this is the case, an attempt is made to ensure that each * sub-pool has the required minimum number of instances during idle object * eviction runs. *

* If the configured value of minIdlePerKey is greater than the configured * value for maxIdlePerKey then the value of maxIdlePerKey will be used * instead. * * @param minIdlePerKey The minimum size of the each keyed pool * * @see #getMinIdlePerKey * @see #getMaxIdlePerKey() * @see #setTimeBetweenEvictionRunsMillis */ public void setMinIdlePerKey(final int minIdlePerKey) { this.minIdlePerKey = minIdlePerKey; } @Override protected void toStringAppendFields(final StringBuilder builder) { super.toStringAppendFields(builder); builder.append(", maxIdlePerKey="); builder.append(maxIdlePerKey); builder.append(", minIdlePerKey="); builder.append(minIdlePerKey); builder.append(", maxTotalPerKey="); builder.append(maxTotalPerKey); builder.append(", factory="); builder.append(factory); builder.append(", fairness="); builder.append(fairness); builder.append(", poolMap="); builder.append(poolMap); builder.append(", poolKeyList="); builder.append(poolKeyList); builder.append(", keyLock="); builder.append(keyLock); builder.append(", numTotal="); builder.append(numTotal); builder.append(", evictionKeyIterator="); builder.append(evictionKeyIterator); builder.append(", evictionKey="); builder.append(evictionKey); builder.append(", abandonedConfig="); builder.append(abandonedConfig); } /** * Whether there is at least one thread waiting on this deque, add an pool object. * @param key pool key. * @param idleObjects list of idle pool objects. */ private void whenWaitersAddObject(final K key, final LinkedBlockingDeque> idleObjects) { if (idleObjects.hasTakeWaiters()) { try { addObject(key); } catch (final Exception e) { swallowException(e); } } } /** * @since 2.10.0 */ @Override public void use(final T pooledObject) { final AbandonedConfig abandonedCfg = this.abandonedConfig; if (abandonedCfg != null && abandonedCfg.getUseUsageTracking()) { poolMap.values().stream() .map(pool -> pool.getAllObjects().get(new IdentityWrapper<>(pooledObject))) .filter(Objects::nonNull) .findFirst() .ifPresent(PooledObject::use); } } } GenericKeyedObjectPoolConfig.java000066400000000000000000000145431405425132200357650ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; /** * A simple "struct" encapsulating the configuration for a * {@link GenericKeyedObjectPool}. * *

* This class is not thread-safe; it is only intended to be used to provide * attributes used when creating a pool. *

* * @param Type of element pooled. * @since 2.0 */ public class GenericKeyedObjectPoolConfig extends BaseObjectPoolConfig { /** * The default value for the {@code maxTotalPerKey} configuration attribute. * @see GenericKeyedObjectPool#getMaxTotalPerKey() */ public static final int DEFAULT_MAX_TOTAL_PER_KEY = 8; /** * The default value for the {@code maxTotal} configuration attribute. * @see GenericKeyedObjectPool#getMaxTotal() */ public static final int DEFAULT_MAX_TOTAL = -1; /** * The default value for the {@code minIdlePerKey} configuration attribute. * @see GenericKeyedObjectPool#getMinIdlePerKey() */ public static final int DEFAULT_MIN_IDLE_PER_KEY = 0; /** * The default value for the {@code maxIdlePerKey} configuration attribute. * @see GenericKeyedObjectPool#getMaxIdlePerKey() */ public static final int DEFAULT_MAX_IDLE_PER_KEY = 8; private int minIdlePerKey = DEFAULT_MIN_IDLE_PER_KEY; private int maxIdlePerKey = DEFAULT_MAX_IDLE_PER_KEY; private int maxTotalPerKey = DEFAULT_MAX_TOTAL_PER_KEY; private int maxTotal = DEFAULT_MAX_TOTAL; /** * Create a new configuration with default settings. */ public GenericKeyedObjectPoolConfig() { } @SuppressWarnings("unchecked") @Override public GenericKeyedObjectPoolConfig clone() { try { return (GenericKeyedObjectPoolConfig) super.clone(); } catch (final CloneNotSupportedException e) { throw new AssertionError(); // Can't happen } } /** * Get the value for the {@code maxIdlePerKey} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code maxIdlePerKey} for this * configuration instance * * @see GenericKeyedObjectPool#getMaxIdlePerKey() */ public int getMaxIdlePerKey() { return maxIdlePerKey; } /** * Get the value for the {@code maxTotal} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code maxTotal} for this * configuration instance * * @see GenericKeyedObjectPool#getMaxTotal() */ public int getMaxTotal() { return maxTotal; } /** * Get the value for the {@code maxTotalPerKey} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code maxTotalPerKey} for this * configuration instance * * @see GenericKeyedObjectPool#getMaxTotalPerKey() */ public int getMaxTotalPerKey() { return maxTotalPerKey; } /** * Get the value for the {@code minIdlePerKey} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code minIdlePerKey} for this * configuration instance * * @see GenericKeyedObjectPool#getMinIdlePerKey() */ public int getMinIdlePerKey() { return minIdlePerKey; } /** * Set the value for the {@code maxIdlePerKey} configuration attribute for * pools created with this configuration instance. * * @param maxIdlePerKey The new setting of {@code maxIdlePerKey} * for this configuration instance * * @see GenericKeyedObjectPool#setMaxIdlePerKey(int) */ public void setMaxIdlePerKey(final int maxIdlePerKey) { this.maxIdlePerKey = maxIdlePerKey; } /** * Set the value for the {@code maxTotal} configuration attribute for * pools created with this configuration instance. * * @param maxTotal The new setting of {@code maxTotal} * for this configuration instance * * @see GenericKeyedObjectPool#setMaxTotal(int) */ public void setMaxTotal(final int maxTotal) { this.maxTotal = maxTotal; } /** * Set the value for the {@code maxTotalPerKey} configuration attribute for * pools created with this configuration instance. * * @param maxTotalPerKey The new setting of {@code maxTotalPerKey} * for this configuration instance * * @see GenericKeyedObjectPool#setMaxTotalPerKey(int) */ public void setMaxTotalPerKey(final int maxTotalPerKey) { this.maxTotalPerKey = maxTotalPerKey; } /** * Set the value for the {@code minIdlePerKey} configuration attribute for * pools created with this configuration instance. * * @param minIdlePerKey The new setting of {@code minIdlePerKey} * for this configuration instance * * @see GenericKeyedObjectPool#setMinIdlePerKey(int) */ public void setMinIdlePerKey(final int minIdlePerKey) { this.minIdlePerKey = minIdlePerKey; } @Override protected void toStringAppendFields(final StringBuilder builder) { super.toStringAppendFields(builder); builder.append(", minIdlePerKey="); builder.append(minIdlePerKey); builder.append(", maxIdlePerKey="); builder.append(maxIdlePerKey); builder.append(", maxTotalPerKey="); builder.append(maxTotalPerKey); builder.append(", maxTotal="); builder.append(maxTotal); } } GenericKeyedObjectPoolMXBean.java000066400000000000000000000214661405425132200356740ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.util.List; import java.util.Map; /** * Defines the methods that will be made available via JMX. * * NOTE: This interface exists only to define those attributes and methods that * will be made available via JMX. It must not be implemented by clients * as it is subject to change between major, minor and patch version * releases of commons pool. Clients that implement this interface may * not, therefore, be able to upgrade to a new minor or patch release * without requiring code changes. * * @param The type of keys maintained by the pool. * * @since 2.0 */ public interface GenericKeyedObjectPoolMXBean { // Expose getters for configuration settings /** * See {@link GenericKeyedObjectPool#getBlockWhenExhausted()} * @return See {@link GenericKeyedObjectPool#getBlockWhenExhausted()} */ boolean getBlockWhenExhausted(); /** * See {@link GenericKeyedObjectPool#getBorrowedCount()} * @return See {@link GenericKeyedObjectPool#getBorrowedCount()} */ long getBorrowedCount(); /** * See {@link GenericKeyedObjectPool#getCreatedCount()} * @return See {@link GenericKeyedObjectPool#getCreatedCount()} */ long getCreatedCount(); /** * See {@link GenericKeyedObjectPool#getCreationStackTrace()} * @return See {@link GenericKeyedObjectPool#getCreationStackTrace()} */ String getCreationStackTrace(); /** * See {@link GenericKeyedObjectPool#getDestroyedByBorrowValidationCount()} * @return See {@link GenericKeyedObjectPool#getDestroyedByBorrowValidationCount()} */ long getDestroyedByBorrowValidationCount(); /** * See {@link GenericKeyedObjectPool#getDestroyedByEvictorCount()} * @return See {@link GenericKeyedObjectPool#getDestroyedByEvictorCount()} */ long getDestroyedByEvictorCount(); /** * See {@link GenericKeyedObjectPool#getDestroyedCount()} * @return See {@link GenericKeyedObjectPool#getDestroyedCount()} */ long getDestroyedCount(); /** * See {@link GenericKeyedObjectPool#getFairness()} * @return See {@link GenericKeyedObjectPool#getFairness()} */ boolean getFairness(); /** * See {@link GenericKeyedObjectPool#getLifo()} * @return See {@link GenericKeyedObjectPool#getLifo()} */ boolean getLifo(); /** * See {@link GenericKeyedObjectPool#getLogAbandoned()} * @return See {@link GenericKeyedObjectPool#getLogAbandoned()} * @since 2.10.0 */ default boolean getLogAbandoned() { return false; } /** * See {@link GenericKeyedObjectPool#getMaxBorrowWaitTimeMillis()} * @return See {@link GenericKeyedObjectPool#getMaxBorrowWaitTimeMillis()} */ long getMaxBorrowWaitTimeMillis(); /** * See {@link GenericKeyedObjectPool#getMaxIdlePerKey()} * @return See {@link GenericKeyedObjectPool#getMaxIdlePerKey()} */ int getMaxIdlePerKey(); /** * See {@link GenericKeyedObjectPool#getMaxTotal()} * @return See {@link GenericKeyedObjectPool#getMaxTotal()} */ int getMaxTotal(); /** * See {@link GenericKeyedObjectPool#getMaxTotalPerKey()} * @return See {@link GenericKeyedObjectPool#getMaxTotalPerKey()} */ int getMaxTotalPerKey(); /** * See {@link GenericKeyedObjectPool#getMaxWaitMillis()} * @return See {@link GenericKeyedObjectPool#getMaxWaitMillis()} */ long getMaxWaitMillis(); /** * See {@link GenericKeyedObjectPool#getMeanActiveTimeMillis()} * @return See {@link GenericKeyedObjectPool#getMeanActiveTimeMillis()} */ long getMeanActiveTimeMillis(); /** * See {@link GenericKeyedObjectPool#getMaxBorrowWaitTimeMillis()} * @return See {@link GenericKeyedObjectPool#getMaxBorrowWaitTimeMillis()} */ long getMeanBorrowWaitTimeMillis(); /** * See {@link GenericKeyedObjectPool#getMeanIdleTimeMillis()} * @return See {@link GenericKeyedObjectPool#getMeanIdleTimeMillis()} */ long getMeanIdleTimeMillis(); /** * See {@link GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()} * @return See {@link GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()} */ long getMinEvictableIdleTimeMillis(); // Expose getters for monitoring attributes /** * See {@link GenericKeyedObjectPool#getMinIdlePerKey()} * @return See {@link GenericKeyedObjectPool#getMinIdlePerKey()} */ int getMinIdlePerKey(); /** * See {@link GenericKeyedObjectPool#getNumActive()} * @return See {@link GenericKeyedObjectPool#getNumActive()} */ int getNumActive(); /** * See {@link GenericKeyedObjectPool#getNumActivePerKey()} * @return See {@link GenericKeyedObjectPool#getNumActivePerKey()} */ Map getNumActivePerKey(); /** * See {@link GenericKeyedObjectPool#getNumIdle()} * @return See {@link GenericKeyedObjectPool#getNumIdle()} */ int getNumIdle(); /** * See {@link GenericKeyedObjectPool#getNumTestsPerEvictionRun()} * @return See {@link GenericKeyedObjectPool#getNumTestsPerEvictionRun()} */ int getNumTestsPerEvictionRun(); /** * See {@link GenericKeyedObjectPool#getNumWaiters()} * @return See {@link GenericKeyedObjectPool#getNumWaiters()} */ int getNumWaiters(); /** * See {@link GenericKeyedObjectPool#getNumWaitersByKey()} * @return See {@link GenericKeyedObjectPool#getNumWaitersByKey()} */ Map getNumWaitersByKey(); /** * See {@link GenericKeyedObjectPool#getRemoveAbandonedOnBorrow()} * @return See {@link GenericKeyedObjectPool#getRemoveAbandonedOnBorrow()} * @since 2.10.0 */ default boolean getRemoveAbandonedOnBorrow() { return false; } /** * See {@link GenericKeyedObjectPool#getRemoveAbandonedOnMaintenance()} * @return See {@link GenericKeyedObjectPool#getRemoveAbandonedOnMaintenance()} * @since 2.10.0 */ default boolean getRemoveAbandonedOnMaintenance() { return false; } /** * See {@link GenericKeyedObjectPool#getRemoveAbandonedTimeout()} * @return See {@link GenericKeyedObjectPool#getRemoveAbandonedTimeout()} * @since 2.10.0 */ default int getRemoveAbandonedTimeout() { return 0; } /** * See {@link GenericKeyedObjectPool#getReturnedCount()} * @return See {@link GenericKeyedObjectPool#getReturnedCount()} */ long getReturnedCount(); /** * See {@link GenericKeyedObjectPool#getTestOnBorrow()} * @return See {@link GenericKeyedObjectPool#getTestOnBorrow()} */ boolean getTestOnBorrow(); /** * See {@link GenericKeyedObjectPool#getTestOnCreate()} * @return See {@link GenericKeyedObjectPool#getTestOnCreate()} * @since 2.2 */ boolean getTestOnCreate(); /** * See {@link GenericKeyedObjectPool#getTestOnReturn()} * @return See {@link GenericKeyedObjectPool#getTestOnReturn()} */ boolean getTestOnReturn(); /** * See {@link GenericKeyedObjectPool#getTestWhileIdle()} * @return See {@link GenericKeyedObjectPool#getTestWhileIdle()} */ boolean getTestWhileIdle(); /** * See {@link GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis()} * @return See {@link GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis()} */ long getTimeBetweenEvictionRunsMillis(); /** * See {@link GenericKeyedObjectPool#isAbandonedConfig()} * @return See {@link GenericKeyedObjectPool#isAbandonedConfig()} * @since 2.10.0 */ default boolean isAbandonedConfig() { return false; } /** * See {@link GenericKeyedObjectPool#isClosed()} * @return See {@link GenericKeyedObjectPool#isClosed()} */ boolean isClosed(); /** * See {@link GenericKeyedObjectPool#listAllObjects()} * @return See {@link GenericKeyedObjectPool#listAllObjects()} */ Map> listAllObjects(); } GenericObjectPool.java000066400000000000000000001445311405425132200336560ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.time.Duration; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.pool2.DestroyMode; import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.PoolUtils; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.PooledObjectState; import org.apache.commons.pool2.SwallowedExceptionListener; import org.apache.commons.pool2.TrackedUse; import org.apache.commons.pool2.UsageTracking; /** * A configurable {@link ObjectPool} implementation. *

* When coupled with the appropriate {@link PooledObjectFactory}, * {@code GenericObjectPool} provides robust pooling functionality for * arbitrary objects. *

*

* Optionally, one may configure the pool to examine and possibly evict objects * as they sit idle in the pool and to ensure that a minimum number of idle * objects are available. This is performed by an "idle object eviction" thread, * which runs asynchronously. Caution should be used when configuring this * optional feature. Eviction runs contend with client threads for access to * objects in the pool, so if they run too frequently performance issues may * result. *

*

* The pool can also be configured to detect and remove "abandoned" objects, * i.e. objects that have been checked out of the pool but neither used nor * returned before the configured * {@link AbandonedConfig#getRemoveAbandonedTimeout() removeAbandonedTimeout}. * Abandoned object removal can be configured to happen when * {@code borrowObject} is invoked and the pool is close to starvation, or * it can be executed by the idle object evictor, or both. If pooled objects * implement the {@link TrackedUse} interface, their last use will be queried * using the {@code getLastUsed} method on that interface; otherwise * abandonment is determined by how long an object has been checked out from * the pool. *

*

* Implementation note: To prevent possible deadlocks, care has been taken to * ensure that no call to a factory method will occur within a synchronization * block. See POOL-125 and DBCP-44 for more information. *

*

* This class is intended to be thread-safe. *

* * @see GenericKeyedObjectPool * * @param Type of element pooled in this pool. * * @since 2.0 */ public class GenericObjectPool extends BaseGenericObjectPool implements ObjectPool, GenericObjectPoolMXBean, UsageTracking { private static final Duration DEFAULT_REMOVE_ABANDONED_TIMEOUT = Duration.ofSeconds(Integer.MAX_VALUE); // JMX specific attributes private static final String ONAME_BASE = "org.apache.commons.pool2:type=GenericObjectPool,name="; private volatile String factoryType = null; private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE; private volatile int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE; private final PooledObjectFactory factory; /* * All of the objects currently associated with this pool in any state. It * excludes objects that have been destroyed. The size of * {@link #allObjects} will always be less than or equal to {@link * #_maxActive}. Map keys are pooled objects, values are the PooledObject * wrappers used internally by the pool. */ private final Map, PooledObject> allObjects = new ConcurrentHashMap<>(); /* * The combined count of the currently created objects and those in the * process of being created. Under load, it may exceed {@link #_maxActive} * if multiple threads try and create a new object at the same time but * {@link #create()} will ensure that there are never more than * {@link #_maxActive} objects created at any one time. */ private final AtomicLong createCount = new AtomicLong(0); private long makeObjectCount; private final Object makeObjectCountLock = new Object(); private final LinkedBlockingDeque> idleObjects; // Additional configuration properties for abandoned object tracking private volatile AbandonedConfig abandonedConfig; /** * Creates a new {@code GenericObjectPool} using defaults from * {@link GenericObjectPoolConfig}. * * @param factory The object factory to be used to create object instances * used by this pool */ public GenericObjectPool(final PooledObjectFactory factory) { this(factory, new GenericObjectPoolConfig<>()); } /** * Creates a new {@code GenericObjectPool} using a specific * configuration. * * @param factory The object factory to be used to create object instances * used by this pool * @param config The configuration to use for this pool instance. The * configuration is used by value. Subsequent changes to * the configuration object will not be reflected in the * pool. */ public GenericObjectPool(final PooledObjectFactory factory, final GenericObjectPoolConfig config) { super(config, ONAME_BASE, config.getJmxNamePrefix()); if (factory == null) { jmxUnregister(); // tidy up throw new IllegalArgumentException("factory may not be null"); } this.factory = factory; idleObjects = new LinkedBlockingDeque<>(config.getFairness()); setConfig(config); } /** * Creates a new {@code GenericObjectPool} that tracks and destroys * objects that are checked out, but never returned to the pool. * * @param factory The object factory to be used to create object instances * used by this pool * @param config The base pool configuration to use for this pool instance. * The configuration is used by value. Subsequent changes to * the configuration object will not be reflected in the * pool. * @param abandonedConfig Configuration for abandoned object identification * and removal. The configuration is used by value. */ public GenericObjectPool(final PooledObjectFactory factory, final GenericObjectPoolConfig config, final AbandonedConfig abandonedConfig) { this(factory, config); setAbandonedConfig(abandonedConfig); } /** * Adds the provided wrapped pooled object to the set of idle objects for * this pool. The object must already be part of the pool. If {@code p} * is null, this is a no-op (no exception, but no impact on the pool). * * @param p The object to make idle * * @throws Exception If the factory fails to passivate the object */ private void addIdleObject(final PooledObject p) throws Exception { if (p != null) { factory.passivateObject(p); if (getLifo()) { idleObjects.addFirst(p); } else { idleObjects.addLast(p); } } } /** * Creates an object, and place it into the pool. addObject() is useful for * "pre-loading" a pool with idle objects. *

* If there is no capacity available to add to the pool, this is a no-op * (no exception, no impact to the pool).

*/ @Override public void addObject() throws Exception { assertOpen(); if (factory == null) { throw new IllegalStateException( "Cannot add objects without a factory."); } final PooledObject p = create(); addIdleObject(p); } /** * Equivalent to {@link #borrowObject(long) * borrowObject}({@link #getMaxWaitMillis()}). *

* {@inheritDoc} *

*/ @Override public T borrowObject() throws Exception { return borrowObject(getMaxWaitMillis()); } /** * Borrows an object from the pool using the specific waiting time which only * applies if {@link #getBlockWhenExhausted()} is true. *

* If there is one or more idle instance available in the pool, then an * idle instance will be selected based on the value of {@link #getLifo()}, * activated and returned. If activation fails, or {@link #getTestOnBorrow() * testOnBorrow} is set to {@code true} and validation fails, the * instance is destroyed and the next available instance is examined. This * continues until either a valid instance is returned or there are no more * idle instances available. *

*

* If there are no idle instances available in the pool, behavior depends on * the {@link #getMaxTotal() maxTotal}, (if applicable) * {@link #getBlockWhenExhausted()} and the value passed in to the * {@code borrowMaxWaitMillis} parameter. If the number of instances * checked out from the pool is less than {@code maxTotal,} a new * instance is created, activated and (if applicable) validated and returned * to the caller. If validation fails, a {@code NoSuchElementException} * is thrown. *

*

* If the pool is exhausted (no available idle instances and no capacity to * create new ones), this method will either block (if * {@link #getBlockWhenExhausted()} is true) or throw a * {@code NoSuchElementException} (if * {@link #getBlockWhenExhausted()} is false). The length of time that this * method will block when {@link #getBlockWhenExhausted()} is true is * determined by the value passed in to the {@code borrowMaxWaitMillis} * parameter. *

*

* When the pool is exhausted, multiple calling threads may be * simultaneously blocked waiting for instances to become available. A * "fairness" algorithm has been implemented to ensure that threads receive * available instances in request arrival order. *

* * @param borrowMaxWait The time to wait for an object * to become available * * @return object instance from the pool * * @throws NoSuchElementException if an instance cannot be returned * * @throws Exception if an object instance cannot be returned due to an * error * @since 2.10.0 */ public T borrowObject(final Duration borrowMaxWait) throws Exception { assertOpen(); final AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getRemoveAbandonedOnBorrow() && (getNumIdle() < 2) && (getNumActive() > getMaxTotal() - 3)) { removeAbandoned(ac); } PooledObject p = null; // Get local copy of current config so it is consistent for entire // method execution final boolean blockWhenExhausted = getBlockWhenExhausted(); boolean create; final long waitTimeMillis = System.currentTimeMillis(); while (p == null) { create = false; p = idleObjects.pollFirst(); if (p == null) { p = create(); if (p != null) { create = true; } } if (blockWhenExhausted) { if (p == null) { if (borrowMaxWait.isNegative()) { p = idleObjects.takeFirst(); } else { p = idleObjects.pollFirst(borrowMaxWait); } } if (p == null) { throw new NoSuchElementException("Timeout waiting for idle object"); } } else if (p == null) { throw new NoSuchElementException("Pool exhausted"); } if (!p.allocate()) { p = null; } if (p != null) { try { factory.activateObject(p); } catch (final Exception e) { try { destroy(p, DestroyMode.NORMAL); } catch (final Exception e1) { // Ignore - activation failure is more important } p = null; if (create) { final NoSuchElementException nsee = new NoSuchElementException("Unable to activate object"); nsee.initCause(e); throw nsee; } } if (p != null && getTestOnBorrow()) { boolean validate = false; Throwable validationThrowable = null; try { validate = factory.validateObject(p); } catch (final Throwable t) { PoolUtils.checkRethrow(t); validationThrowable = t; } if (!validate) { try { destroy(p, DestroyMode.NORMAL); destroyedByBorrowValidationCount.incrementAndGet(); } catch (final Exception e) { // Ignore - validation failure is more important } p = null; if (create) { final NoSuchElementException nsee = new NoSuchElementException("Unable to validate object"); nsee.initCause(validationThrowable); throw nsee; } } } } } updateStatsBorrow(p, Duration.ofMillis(System.currentTimeMillis() - waitTimeMillis)); return p.getObject(); } /** * Borrows an object from the pool using the specific waiting time which only * applies if {@link #getBlockWhenExhausted()} is true. *

* If there is one or more idle instance available in the pool, then an * idle instance will be selected based on the value of {@link #getLifo()}, * activated and returned. If activation fails, or {@link #getTestOnBorrow() * testOnBorrow} is set to {@code true} and validation fails, the * instance is destroyed and the next available instance is examined. This * continues until either a valid instance is returned or there are no more * idle instances available. *

*

* If there are no idle instances available in the pool, behavior depends on * the {@link #getMaxTotal() maxTotal}, (if applicable) * {@link #getBlockWhenExhausted()} and the value passed in to the * {@code borrowMaxWaitMillis} parameter. If the number of instances * checked out from the pool is less than {@code maxTotal,} a new * instance is created, activated and (if applicable) validated and returned * to the caller. If validation fails, a {@code NoSuchElementException} * is thrown. *

*

* If the pool is exhausted (no available idle instances and no capacity to * create new ones), this method will either block (if * {@link #getBlockWhenExhausted()} is true) or throw a * {@code NoSuchElementException} (if * {@link #getBlockWhenExhausted()} is false). The length of time that this * method will block when {@link #getBlockWhenExhausted()} is true is * determined by the value passed in to the {@code borrowMaxWaitMillis} * parameter. *

*

* When the pool is exhausted, multiple calling threads may be * simultaneously blocked waiting for instances to become available. A * "fairness" algorithm has been implemented to ensure that threads receive * available instances in request arrival order. *

* * @param borrowMaxWaitMillis The time to wait in milliseconds for an object * to become available * * @return object instance from the pool * * @throws NoSuchElementException if an instance cannot be returned * * @throws Exception if an object instance cannot be returned due to an * error */ public T borrowObject(final long borrowMaxWaitMillis) throws Exception { return borrowObject(Duration.ofMillis(borrowMaxWaitMillis)); } /** * Clears any objects sitting idle in the pool by removing them from the * idle instance pool and then invoking the configured * {@link PooledObjectFactory#destroyObject(PooledObject)} method on each * idle instance. *

* Implementation notes: *

*
    *
  • This method does not destroy or effect in any way instances that are * checked out of the pool when it is invoked.
  • *
  • Invoking this method does not prevent objects being returned to the * idle instance pool, even during its execution. Additional instances may * be returned while removed items are being destroyed.
  • *
  • Exceptions encountered destroying idle instances are swallowed * but notified via a {@link SwallowedExceptionListener}.
  • *
*/ @Override public void clear() { PooledObject p = idleObjects.poll(); while (p != null) { try { destroy(p, DestroyMode.NORMAL); } catch (final Exception e) { swallowException(e); } p = idleObjects.poll(); } } /** * Closes the pool. Once the pool is closed, {@link #borrowObject()} will * fail with IllegalStateException, but {@link #returnObject(Object)} and * {@link #invalidateObject(Object)} will continue to work, with returned * objects destroyed on return. *

* Destroys idle instances in the pool by invoking {@link #clear()}. *

*/ @Override public void close() { if (isClosed()) { return; } synchronized (closeLock) { if (isClosed()) { return; } // Stop the evictor before the pool is closed since evict() calls // assertOpen() stopEvictor(); closed = true; // This clear removes any idle objects clear(); jmxUnregister(); // Release any threads that were waiting for an object idleObjects.interuptTakeWaiters(); } } /** * Attempts to create a new wrapped pooled object. *

* If there are {@link #getMaxTotal()} objects already in circulation * or in process of being created, this method returns null. *

* * @return The new wrapped pooled object * * @throws Exception if the object factory's {@code makeObject} fails */ private PooledObject create() throws Exception { int localMaxTotal = getMaxTotal(); // This simplifies the code later in this method if (localMaxTotal < 0) { localMaxTotal = Integer.MAX_VALUE; } final long localStartTimeMillis = System.currentTimeMillis(); final long localMaxWaitTimeMillis = Math.max(getMaxWaitMillis(), 0); // Flag that indicates if create should: // - TRUE: call the factory to create an object // - FALSE: return null // - null: loop and re-test the condition that determines whether to // call the factory Boolean create = null; while (create == null) { synchronized (makeObjectCountLock) { final long newCreateCount = createCount.incrementAndGet(); if (newCreateCount > localMaxTotal) { // The pool is currently at capacity or in the process of // making enough new objects to take it to capacity. createCount.decrementAndGet(); if (makeObjectCount == 0) { // There are no makeObject() calls in progress so the // pool is at capacity. Do not attempt to create a new // object. Return and wait for an object to be returned create = Boolean.FALSE; } else { // There are makeObject() calls in progress that might // bring the pool to capacity. Those calls might also // fail so wait until they complete and then re-test if // the pool is at capacity or not. makeObjectCountLock.wait(localMaxWaitTimeMillis); } } else { // The pool is not at capacity. Create a new object. makeObjectCount++; create = Boolean.TRUE; } } // Do not block more if maxWaitTimeMillis is set. if (create == null && (localMaxWaitTimeMillis > 0 && System.currentTimeMillis() - localStartTimeMillis >= localMaxWaitTimeMillis)) { create = Boolean.FALSE; } } if (!create.booleanValue()) { return null; } final PooledObject p; try { p = factory.makeObject(); if (getTestOnCreate() && !factory.validateObject(p)) { createCount.decrementAndGet(); return null; } } catch (final Throwable e) { createCount.decrementAndGet(); throw e; } finally { synchronized (makeObjectCountLock) { makeObjectCount--; makeObjectCountLock.notifyAll(); } } final AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getLogAbandoned()) { p.setLogAbandoned(true); p.setRequireFullStackTrace(ac.getRequireFullStackTrace()); } createdCount.incrementAndGet(); allObjects.put(new IdentityWrapper<>(p.getObject()), p); return p; } /** * Destroys a wrapped pooled object. * * @param toDestroy The wrapped pooled object to destroy * @param mode DestroyMode context provided to the factory * * @throws Exception If the factory fails to destroy the pooled object * cleanly */ private void destroy(final PooledObject toDestroy, final DestroyMode mode) throws Exception { toDestroy.invalidate(); idleObjects.remove(toDestroy); allObjects.remove(new IdentityWrapper<>(toDestroy.getObject())); try { factory.destroyObject(toDestroy, mode); } finally { destroyedCount.incrementAndGet(); createCount.decrementAndGet(); } } /** * Tries to ensure that {@code idleCount} idle instances exist in the pool. *

* Creates and adds idle instances until either {@link #getNumIdle()} reaches {@code idleCount} * or the total number of objects (idle, checked out, or being created) reaches * {@link #getMaxTotal()}. If {@code always} is false, no instances are created unless * there are threads waiting to check out instances from the pool. *

* * @param idleCount the number of idle instances desired * @param always true means create instances even if the pool has no threads waiting * @throws Exception if the factory's makeObject throws */ private void ensureIdle(final int idleCount, final boolean always) throws Exception { if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) { return; } while (idleObjects.size() < idleCount) { final PooledObject p = create(); if (p == null) { // Can't create objects, no reason to think another call to // create will work. Give up. break; } if (getLifo()) { idleObjects.addFirst(p); } else { idleObjects.addLast(p); } } if (isClosed()) { // Pool closed while object was being added to idle objects. // Make sure the returned object is destroyed rather than left // in the idle object pool (which would effectively be a leak) clear(); } } @Override void ensureMinIdle() throws Exception { ensureIdle(getMinIdle(), true); } /** * {@inheritDoc} *

* Successive activations of this method examine objects in sequence, * cycling through objects in oldest-to-youngest order. *

*/ @Override public void evict() throws Exception { assertOpen(); if (!idleObjects.isEmpty()) { PooledObject underTest = null; final EvictionPolicy evictionPolicy = getEvictionPolicy(); synchronized (evictionLock) { final EvictionConfig evictionConfig = new EvictionConfig( getMinEvictableIdleTime(), getSoftMinEvictableIdleTime(), getMinIdle()); final boolean testWhileIdle = getTestWhileIdle(); for (int i = 0, m = getNumTests(); i < m; i++) { if (evictionIterator == null || !evictionIterator.hasNext()) { evictionIterator = new EvictionIterator(idleObjects); } if (!evictionIterator.hasNext()) { // Pool exhausted, nothing to do here return; } try { underTest = evictionIterator.next(); } catch (final NoSuchElementException nsee) { // Object was borrowed in another thread // Don't count this as an eviction test so reduce i; i--; evictionIterator = null; continue; } if (!underTest.startEvictionTest()) { // Object was borrowed in another thread // Don't count this as an eviction test so reduce i; i--; continue; } // User provided eviction policy could throw all sorts of // crazy exceptions. Protect against such an exception // killing the eviction thread. boolean evict; try { evict = evictionPolicy.evict(evictionConfig, underTest, idleObjects.size()); } catch (final Throwable t) { // Slightly convoluted as SwallowedExceptionListener // uses Exception rather than Throwable PoolUtils.checkRethrow(t); swallowException(new Exception(t)); // Don't evict on error conditions evict = false; } if (evict) { destroy(underTest, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); } else { if (testWhileIdle) { boolean active = false; try { factory.activateObject(underTest); active = true; } catch (final Exception e) { destroy(underTest, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); } if (active) { if (!factory.validateObject(underTest)) { destroy(underTest, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); } else { try { factory.passivateObject(underTest); } catch (final Exception e) { destroy(underTest, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); } } } } if (!underTest.endEvictionTest(idleObjects)) { // TODO - May need to add code here once additional // states are used } } } } } final AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getRemoveAbandonedOnMaintenance()) { removeAbandoned(ac); } } /** * Gets a reference to the factory used to create, destroy and validate * the objects used by this pool. * * @return the factory */ public PooledObjectFactory getFactory() { return factory; } /** * Gets the type - including the specific type rather than the generic - * of the factory. * * @return A string representation of the factory type */ @Override public String getFactoryType() { // Not thread safe. Accept that there may be multiple evaluations. if (factoryType == null) { final StringBuilder result = new StringBuilder(); result.append(factory.getClass().getName()); result.append('<'); final Class pooledObjectType = PoolImplUtils.getFactoryType(factory.getClass()); result.append(pooledObjectType.getName()); result.append('>'); factoryType = result.toString(); } return factoryType; } /** * Gets whether this pool identifies and logs any abandoned objects. * * @return {@code true} if abandoned object removal is configured for this * pool and removal events are to be logged otherwise {@code false} * * @see AbandonedConfig#getLogAbandoned() */ @Override public boolean getLogAbandoned() { final AbandonedConfig ac = this.abandonedConfig; return ac != null && ac.getLogAbandoned(); } /** * Gets the cap on the number of "idle" instances in the pool. If maxIdle * is set too low on heavily loaded systems it is possible you will see * objects being destroyed and almost immediately new objects being created. * This is a result of the active threads momentarily returning objects * faster than they are requesting them, causing the number of idle * objects to rise above maxIdle. The best value for maxIdle for heavily * loaded system will vary but the default is a good starting point. * * @return the maximum number of "idle" instances that can be held in the * pool or a negative value if there is no limit * * @see #setMaxIdle */ @Override public int getMaxIdle() { return maxIdle; } /** * Gets the target for the minimum number of idle objects to maintain in * the pool. This setting only has an effect if it is positive and * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this * is the case, an attempt is made to ensure that the pool has the required * minimum number of instances during idle object eviction runs. *

* If the configured value of minIdle is greater than the configured value * for maxIdle then the value of maxIdle will be used instead. *

* * @return The minimum number of objects. * * @see #setMinIdle(int) * @see #setMaxIdle(int) * @see #setTimeBetweenEvictionRunsMillis(long) */ @Override public int getMinIdle() { final int maxIdleSave = getMaxIdle(); if (this.minIdle > maxIdleSave) { return maxIdleSave; } return minIdle; } @Override public int getNumActive() { return allObjects.size() - idleObjects.size(); } @Override public int getNumIdle() { return idleObjects.size(); } /** * Calculates the number of objects to test in a run of the idle object * evictor. * * @return The number of objects to test for validity */ private int getNumTests() { final int numTestsPerEvictionRun = getNumTestsPerEvictionRun(); if (numTestsPerEvictionRun >= 0) { return Math.min(numTestsPerEvictionRun, idleObjects.size()); } return (int) (Math.ceil(idleObjects.size() / Math.abs((double) numTestsPerEvictionRun))); } /** * Gets an estimate of the number of threads currently blocked waiting for * an object from the pool. This is intended for monitoring only, not for * synchronization control. * * @return The estimate of the number of threads currently blocked waiting * for an object from the pool */ @Override public int getNumWaiters() { if (getBlockWhenExhausted()) { return idleObjects.getTakeQueueLength(); } return 0; } /** * Gets whether a check is made for abandoned objects when an object is borrowed * from this pool. * * @return {@code true} if abandoned object removal is configured to be * activated by borrowObject otherwise {@code false} * * @see AbandonedConfig#getRemoveAbandonedOnBorrow() */ @Override public boolean getRemoveAbandonedOnBorrow() { final AbandonedConfig ac = this.abandonedConfig; return ac != null && ac.getRemoveAbandonedOnBorrow(); } //--- Usage tracking support ----------------------------------------------- /** * Gets whether a check is made for abandoned objects when the evictor runs. * * @return {@code true} if abandoned object removal is configured to be * activated when the evictor runs otherwise {@code false} * * @see AbandonedConfig#getRemoveAbandonedOnMaintenance() */ @Override public boolean getRemoveAbandonedOnMaintenance() { final AbandonedConfig ac = this.abandonedConfig; return ac != null && ac.getRemoveAbandonedOnMaintenance(); } //--- JMX support ---------------------------------------------------------- /** * Gets the timeout before which an object will be considered to be * abandoned by this pool. * * @return The abandoned object timeout in seconds if abandoned object * removal is configured for this pool; Integer.MAX_VALUE otherwise. * * @see AbandonedConfig#getRemoveAbandonedTimeout() * @see AbandonedConfig#getRemoveAbandonedTimeoutDuration() * @deprecated Use {@link #getRemoveAbandonedTimeoutDuration()}. */ @Override @Deprecated public int getRemoveAbandonedTimeout() { final AbandonedConfig ac = this.abandonedConfig; return ac != null ? ac.getRemoveAbandonedTimeout() : Integer.MAX_VALUE; } /** * Gets the timeout before which an object will be considered to be * abandoned by this pool. * * @return The abandoned object timeout in seconds if abandoned object * removal is configured for this pool; Integer.MAX_VALUE otherwise. * * @see AbandonedConfig#getRemoveAbandonedTimeout() * @see AbandonedConfig#getRemoveAbandonedTimeoutDuration() */ public Duration getRemoveAbandonedTimeoutDuration() { final AbandonedConfig ac = this.abandonedConfig; return ac != null ? ac.getRemoveAbandonedTimeoutDuration() : DEFAULT_REMOVE_ABANDONED_TIMEOUT; } /** * {@inheritDoc} *

* Activation of this method decrements the active count and attempts to * destroy the instance, using the default (NORMAL) {@link DestroyMode}. *

* * @throws Exception if an exception occurs destroying the * object * @throws IllegalStateException if obj does not belong to this pool */ @Override public void invalidateObject(final T obj) throws Exception { invalidateObject(obj, DestroyMode.NORMAL); } /** * {@inheritDoc} *

* Activation of this method decrements the active count and attempts to * destroy the instance, using the provided {@link DestroyMode}. *

* * @throws Exception if an exception occurs destroying the * object * @throws IllegalStateException if obj does not belong to this pool * @since 2.9.0 */ @Override public void invalidateObject(final T obj, final DestroyMode mode) throws Exception { final PooledObject p = allObjects.get(new IdentityWrapper<>(obj)); if (p == null) { if (isAbandonedConfig()) { return; } throw new IllegalStateException( "Invalidated object not currently part of this pool"); } synchronized (p) { if (p.getState() != PooledObjectState.INVALID) { destroy(p, mode); } } ensureIdle(1, false); } // --- configuration attributes -------------------------------------------- /** * Gets whether or not abandoned object removal is configured for this pool. * * @return true if this pool is configured to detect and remove * abandoned objects */ @Override public boolean isAbandonedConfig() { return abandonedConfig != null; } /** * Provides information on all the objects in the pool, both idle (waiting * to be borrowed) and active (currently borrowed). *

* Note: This is named listAllObjects so it is presented as an operation via * JMX. That means it won't be invoked unless the explicitly requested * whereas all attributes will be automatically requested when viewing the * attributes for an object in a tool like JConsole. *

* * @return Information grouped on all the objects in the pool */ @Override public Set listAllObjects() { final Set result = new HashSet<>(allObjects.size()); for (final PooledObject p : allObjects.values()) { result.add(new DefaultPooledObjectInfo(p)); } return result; } /** * Tries to ensure that {@link #getMinIdle()} idle instances are available * in the pool. * * @throws Exception If the associated factory throws an exception * @since 2.4 */ public void preparePool() throws Exception { if (getMinIdle() < 1) { return; } ensureMinIdle(); } // --- internal attributes ------------------------------------------------- /** * Recovers abandoned objects which have been checked out but * not used since longer than the removeAbandonedTimeout. * * @param abandonedConfig The configuration to use to identify abandoned objects */ @SuppressWarnings("resource") // PrintWriter is managed elsewhere private void removeAbandoned(final AbandonedConfig abandonedConfig) { // Generate a list of abandoned objects to remove final long nowMillis = System.currentTimeMillis(); final long timeoutMillis = nowMillis - abandonedConfig.getRemoveAbandonedTimeoutDuration().toMillis(); final ArrayList> remove = new ArrayList<>(); final Iterator> it = allObjects.values().iterator(); while (it.hasNext()) { final PooledObject pooledObject = it.next(); synchronized (pooledObject) { if (pooledObject.getState() == PooledObjectState.ALLOCATED && pooledObject.getLastUsedTime() <= timeoutMillis) { pooledObject.markAbandoned(); remove.add(pooledObject); } } } // Now remove the abandoned objects final Iterator> itr = remove.iterator(); while (itr.hasNext()) { final PooledObject pooledObject = itr.next(); if (abandonedConfig.getLogAbandoned()) { pooledObject.printStackTrace(abandonedConfig.getLogWriter()); } try { invalidateObject(pooledObject.getObject(), DestroyMode.ABANDONED); } catch (final Exception e) { e.printStackTrace(); } } } /** * {@inheritDoc} *

* If {@link #getMaxIdle() maxIdle} is set to a positive value and the * number of idle instances has reached this value, the returning instance * is destroyed. *

*

* If {@link #getTestOnReturn() testOnReturn} == true, the returning * instance is validated before being returned to the idle instance pool. In * this case, if validation fails, the instance is destroyed. *

*

* Exceptions encountered destroying objects for any reason are swallowed * but notified via a {@link SwallowedExceptionListener}. *

*/ @Override public void returnObject(final T obj) { final PooledObject p = allObjects.get(new IdentityWrapper<>(obj)); if (p == null) { if (!isAbandonedConfig()) { throw new IllegalStateException( "Returned object not currently part of this pool"); } return; // Object was abandoned and removed } markReturningState(p); final Duration activeTime = p.getActiveTime(); if (getTestOnReturn() && !factory.validateObject(p)) { try { destroy(p, DestroyMode.NORMAL); } catch (final Exception e) { swallowException(e); } try { ensureIdle(1, false); } catch (final Exception e) { swallowException(e); } updateStatsReturn(activeTime); return; } try { factory.passivateObject(p); } catch (final Exception e1) { swallowException(e1); try { destroy(p, DestroyMode.NORMAL); } catch (final Exception e) { swallowException(e); } try { ensureIdle(1, false); } catch (final Exception e) { swallowException(e); } updateStatsReturn(activeTime); return; } if (!p.deallocate()) { throw new IllegalStateException( "Object has already been returned to this pool or is invalid"); } final int maxIdleSave = getMaxIdle(); if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) { try { destroy(p, DestroyMode.NORMAL); } catch (final Exception e) { swallowException(e); } try { ensureIdle(1, false); } catch (final Exception e) { swallowException(e); } } else { if (getLifo()) { idleObjects.addFirst(p); } else { idleObjects.addLast(p); } if (isClosed()) { // Pool closed while object was being added to idle objects. // Make sure the returned object is destroyed rather than left // in the idle object pool (which would effectively be a leak) clear(); } } updateStatsReturn(activeTime); } /** * Sets the abandoned object removal configuration. * * @param abandonedConfig the new configuration to use. This is used by value. * * @see AbandonedConfig */ @SuppressWarnings("resource") // PrintWriter is managed elsewhere public void setAbandonedConfig(final AbandonedConfig abandonedConfig) { if (abandonedConfig == null) { this.abandonedConfig = null; } else { this.abandonedConfig = new AbandonedConfig(); this.abandonedConfig.setLogAbandoned(abandonedConfig.getLogAbandoned()); this.abandonedConfig.setLogWriter(abandonedConfig.getLogWriter()); this.abandonedConfig.setRemoveAbandonedOnBorrow(abandonedConfig.getRemoveAbandonedOnBorrow()); this.abandonedConfig.setRemoveAbandonedOnMaintenance(abandonedConfig.getRemoveAbandonedOnMaintenance()); this.abandonedConfig.setRemoveAbandonedTimeout(abandonedConfig.getRemoveAbandonedTimeoutDuration()); this.abandonedConfig.setUseUsageTracking(abandonedConfig.getUseUsageTracking()); this.abandonedConfig.setRequireFullStackTrace(abandonedConfig.getRequireFullStackTrace()); } } /** * Sets the base pool configuration. * * @param conf the new configuration to use. This is used by value. * * @see GenericObjectPoolConfig */ public void setConfig(final GenericObjectPoolConfig conf) { super.setConfig(conf); setMaxIdle(conf.getMaxIdle()); setMinIdle(conf.getMinIdle()); setMaxTotal(conf.getMaxTotal()); } /** * Sets the cap on the number of "idle" instances in the pool. If maxIdle * is set too low on heavily loaded systems it is possible you will see * objects being destroyed and almost immediately new objects being created. * This is a result of the active threads momentarily returning objects * faster than they are requesting them, causing the number of idle * objects to rise above maxIdle. The best value for maxIdle for heavily * loaded system will vary but the default is a good starting point. * * @param maxIdle * The cap on the number of "idle" instances in the pool. Use a * negative value to indicate an unlimited number of idle * instances * * @see #getMaxIdle */ public void setMaxIdle(final int maxIdle) { this.maxIdle = maxIdle; } /** * Sets the target for the minimum number of idle objects to maintain in * the pool. This setting only has an effect if it is positive and * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this * is the case, an attempt is made to ensure that the pool has the required * minimum number of instances during idle object eviction runs. *

* If the configured value of minIdle is greater than the configured value * for maxIdle then the value of maxIdle will be used instead. *

* * @param minIdle * The minimum number of objects. * * @see #getMinIdle() * @see #getMaxIdle() * @see #getTimeBetweenEvictionRunsMillis() */ public void setMinIdle(final int minIdle) { this.minIdle = minIdle; } @Override protected void toStringAppendFields(final StringBuilder builder) { super.toStringAppendFields(builder); builder.append(", factoryType="); builder.append(factoryType); builder.append(", maxIdle="); builder.append(maxIdle); builder.append(", minIdle="); builder.append(minIdle); builder.append(", factory="); builder.append(factory); builder.append(", allObjects="); builder.append(allObjects); builder.append(", createCount="); builder.append(createCount); builder.append(", idleObjects="); builder.append(idleObjects); builder.append(", abandonedConfig="); builder.append(abandonedConfig); } @Override public void use(final T pooledObject) { final AbandonedConfig abandonedCfg = this.abandonedConfig; if (abandonedCfg != null && abandonedCfg.getUseUsageTracking()) { final PooledObject wrapper = allObjects.get(new IdentityWrapper<>(pooledObject)); wrapper.use(); } } } GenericObjectPoolConfig.java000066400000000000000000000113551405425132200350010ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; /** * A simple "struct" encapsulating the configuration for a * {@link GenericObjectPool}. * *

* This class is not thread-safe; it is only intended to be used to provide * attributes used when creating a pool. *

* * @param Type of element pooled. * @since 2.0 */ public class GenericObjectPoolConfig extends BaseObjectPoolConfig { /** * The default value for the {@code maxTotal} configuration attribute. * @see GenericObjectPool#getMaxTotal() */ public static final int DEFAULT_MAX_TOTAL = 8; /** * The default value for the {@code maxIdle} configuration attribute. * @see GenericObjectPool#getMaxIdle() */ public static final int DEFAULT_MAX_IDLE = 8; /** * The default value for the {@code minIdle} configuration attribute. * @see GenericObjectPool#getMinIdle() */ public static final int DEFAULT_MIN_IDLE = 0; private int maxTotal = DEFAULT_MAX_TOTAL; private int maxIdle = DEFAULT_MAX_IDLE; private int minIdle = DEFAULT_MIN_IDLE; @SuppressWarnings("unchecked") @Override public GenericObjectPoolConfig clone() { try { return (GenericObjectPoolConfig) super.clone(); } catch (final CloneNotSupportedException e) { throw new AssertionError(); // Can't happen } } /** * Get the value for the {@code maxIdle} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code maxIdle} for this * configuration instance * * @see GenericObjectPool#getMaxIdle() */ public int getMaxIdle() { return maxIdle; } /** * Get the value for the {@code maxTotal} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code maxTotal} for this * configuration instance * * @see GenericObjectPool#getMaxTotal() */ public int getMaxTotal() { return maxTotal; } /** * Get the value for the {@code minIdle} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code minIdle} for this * configuration instance * * @see GenericObjectPool#getMinIdle() */ public int getMinIdle() { return minIdle; } /** * Set the value for the {@code maxIdle} configuration attribute for * pools created with this configuration instance. * * @param maxIdle The new setting of {@code maxIdle} * for this configuration instance * * @see GenericObjectPool#setMaxIdle(int) */ public void setMaxIdle(final int maxIdle) { this.maxIdle = maxIdle; } /** * Set the value for the {@code maxTotal} configuration attribute for * pools created with this configuration instance. * * @param maxTotal The new setting of {@code maxTotal} * for this configuration instance * * @see GenericObjectPool#setMaxTotal(int) */ public void setMaxTotal(final int maxTotal) { this.maxTotal = maxTotal; } /** * Set the value for the {@code minIdle} configuration attribute for * pools created with this configuration instance. * * @param minIdle The new setting of {@code minIdle} * for this configuration instance * * @see GenericObjectPool#setMinIdle(int) */ public void setMinIdle(final int minIdle) { this.minIdle = minIdle; } @Override protected void toStringAppendFields(final StringBuilder builder) { super.toStringAppendFields(builder); builder.append(", maxTotal="); builder.append(maxTotal); builder.append(", maxIdle="); builder.append(maxIdle); builder.append(", minIdle="); builder.append(minIdle); } } GenericObjectPoolMXBean.java000066400000000000000000000173121405425132200347050ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.util.Set; /** * Defines the methods that will be made available via JMX. * * NOTE: This interface exists only to define those attributes and methods that * will be made available via JMX. It must not be implemented by clients * as it is subject to change between major, minor and patch version * releases of commons pool. Clients that implement this interface may * not, therefore, be able to upgrade to a new minor or patch release * without requiring code changes. * * @since 2.0 */ public interface GenericObjectPoolMXBean { // Getters for basic configuration settings /** * See {@link GenericObjectPool#getBlockWhenExhausted()} * @return See {@link GenericObjectPool#getBlockWhenExhausted()} */ boolean getBlockWhenExhausted(); /** * See {@link GenericObjectPool#getBorrowedCount()} * @return See {@link GenericObjectPool#getBorrowedCount()} */ long getBorrowedCount(); /** * See {@link GenericObjectPool#getCreatedCount()} * @return See {@link GenericObjectPool#getCreatedCount()} */ long getCreatedCount(); /** * See {@link GenericObjectPool#getCreationStackTrace()} * @return See {@link GenericObjectPool#getCreationStackTrace()} */ String getCreationStackTrace(); /** * See {@link GenericObjectPool#getDestroyedByBorrowValidationCount()} * @return See {@link GenericObjectPool#getDestroyedByBorrowValidationCount()} */ long getDestroyedByBorrowValidationCount(); /** * See {@link GenericObjectPool#getDestroyedByEvictorCount()} * @return See {@link GenericObjectPool#getDestroyedByEvictorCount()} */ long getDestroyedByEvictorCount(); /** * See {@link GenericObjectPool#getDestroyedCount()} * @return See {@link GenericObjectPool#getDestroyedCount()} */ long getDestroyedCount(); /** * See {@link GenericObjectPool#getFactoryType()} * @return See {@link GenericObjectPool#getFactoryType()} */ String getFactoryType(); /** * See {@link GenericObjectPool#getLifo()} * @return See {@link GenericObjectPool#getLifo()} */ boolean getFairness(); /** * See {@link GenericObjectPool#getFairness()} * @return See {@link GenericObjectPool#getFairness()} */ boolean getLifo(); /** * See {@link GenericObjectPool#getLogAbandoned()} * @return See {@link GenericObjectPool#getLogAbandoned()} */ boolean getLogAbandoned(); /** * See {@link GenericObjectPool#getMaxBorrowWaitTimeMillis()} * @return See {@link GenericObjectPool#getMaxBorrowWaitTimeMillis()} */ long getMaxBorrowWaitTimeMillis(); /** * See {@link GenericObjectPool#getMaxIdle()} * @return See {@link GenericObjectPool#getMaxIdle()} */ int getMaxIdle(); /** * See {@link GenericObjectPool#getMaxTotal()} * @return See {@link GenericObjectPool#getMaxTotal()} */ int getMaxTotal(); /** * See {@link GenericObjectPool#getMaxWaitMillis()} * @return See {@link GenericObjectPool#getMaxWaitMillis()} */ long getMaxWaitMillis(); /** * See {@link GenericObjectPool#getMeanActiveTimeMillis()} * @return See {@link GenericObjectPool#getMeanActiveTimeMillis()} */ long getMeanActiveTimeMillis(); /** * See {@link GenericObjectPool#getMeanBorrowWaitTimeMillis()} * @return See {@link GenericObjectPool#getMeanBorrowWaitTimeMillis()} */ long getMeanBorrowWaitTimeMillis(); // Getters for monitoring attributes /** * See {@link GenericObjectPool#getMeanIdleTimeMillis()} * @return See {@link GenericObjectPool#getMeanIdleTimeMillis()} */ long getMeanIdleTimeMillis(); /** * See {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} * @return See {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} */ long getMinEvictableIdleTimeMillis(); /** * See {@link GenericObjectPool#getMinIdle()} * @return See {@link GenericObjectPool#getMinIdle()} */ int getMinIdle(); /** * See {@link GenericObjectPool#getNumActive()} * @return See {@link GenericObjectPool#getNumActive()} */ int getNumActive(); /** * See {@link GenericObjectPool#getNumIdle()} * @return See {@link GenericObjectPool#getNumIdle()} */ int getNumIdle(); /** * See {@link GenericObjectPool#getNumTestsPerEvictionRun()} * @return See {@link GenericObjectPool#getNumTestsPerEvictionRun()} */ int getNumTestsPerEvictionRun(); /** * See {@link GenericObjectPool#getNumWaiters()} * @return See {@link GenericObjectPool#getNumWaiters()} */ int getNumWaiters(); /** * See {@link GenericObjectPool#getRemoveAbandonedOnBorrow()} * @return See {@link GenericObjectPool#getRemoveAbandonedOnBorrow()} */ boolean getRemoveAbandonedOnBorrow(); /** * See {@link GenericObjectPool#getRemoveAbandonedOnMaintenance()} * @return See {@link GenericObjectPool#getRemoveAbandonedOnMaintenance()} */ boolean getRemoveAbandonedOnMaintenance(); /** * See {@link GenericObjectPool#getRemoveAbandonedTimeout()} * @return See {@link GenericObjectPool#getRemoveAbandonedTimeout()} */ int getRemoveAbandonedTimeout(); /** * See {@link GenericObjectPool#getReturnedCount()} * @return See {@link GenericObjectPool#getReturnedCount()} */ long getReturnedCount(); /** * See {@link GenericObjectPool#getTestOnBorrow()} * @return See {@link GenericObjectPool#getTestOnBorrow()} */ boolean getTestOnBorrow(); // Getters for abandoned object removal configuration /** * See {@link GenericObjectPool#getTestOnCreate()} * @return See {@link GenericObjectPool#getTestOnCreate()} * @since 2.2 */ boolean getTestOnCreate(); /** * See {@link GenericObjectPool#getTestOnReturn()} * @return See {@link GenericObjectPool#getTestOnReturn()} */ boolean getTestOnReturn(); /** * See {@link GenericObjectPool#getTestWhileIdle()} * @return See {@link GenericObjectPool#getTestWhileIdle()} */ boolean getTestWhileIdle(); /** * See {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis()} * @return See {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis()} */ long getTimeBetweenEvictionRunsMillis(); /** * See {@link GenericObjectPool#isAbandonedConfig()} * @return See {@link GenericObjectPool#isAbandonedConfig()} */ boolean isAbandonedConfig(); /** * See {@link GenericObjectPool#isClosed()} * @return See {@link GenericObjectPool#isClosed()} */ boolean isClosed(); /** * See {@link GenericObjectPool#listAllObjects()} * @return See {@link GenericObjectPool#listAllObjects()} */ Set listAllObjects(); } InterruptibleReentrantLock.java000066400000000000000000000040301405425132200356320ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.util.Collection; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * This sub-class was created to expose the waiting threads so that they can be * interrupted when the pool using the queue that uses this lock is closed. The * class is intended for internal use only. *

* This class is intended to be thread-safe. *

* * @since 2.0 */ class InterruptibleReentrantLock extends ReentrantLock { private static final long serialVersionUID = 1L; /** * Create a new InterruptibleReentrantLock with the given fairness policy. * * @param fairness true means threads should acquire contended locks as if * waiting in a FIFO queue */ public InterruptibleReentrantLock(final boolean fairness) { super(fairness); } /** * Interrupt the threads that are waiting on a specific condition * * @param condition the condition on which the threads are waiting. */ public void interruptWaiters(final Condition condition) { final Collection threads = getWaitingThreads(condition); for (final Thread thread : threads) { thread.interrupt(); } } } LinkedBlockingDeque.java000066400000000000000000001271251405425132200341640ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.io.Serializable; import java.time.Duration; import java.util.AbstractQueue; import java.util.Collection; import java.util.Deque; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; /** * An optionally-bounded {@linkplain java.util.concurrent.BlockingDeque blocking * deque} based on linked nodes. * *

The optional capacity bound constructor argument serves as a * way to prevent excessive expansion. The capacity, if unspecified, * is equal to {@link Integer#MAX_VALUE}. Linked nodes are * dynamically created upon each insertion unless this would bring the * deque above capacity. *

* *

Most operations run in constant time (ignoring time spent * blocking). Exceptions include {@link #remove(Object) remove}, * {@link #removeFirstOccurrence removeFirstOccurrence}, {@link * #removeLastOccurrence removeLastOccurrence}, {@link #contains * contains}, {@link #iterator iterator.remove()}, and the bulk * operations, all of which run in linear time. *

* *

This class and its iterator implement all of the * optional methods of the {@link Collection} and {@link * Iterator} interfaces. *

* *

This class is a member of the * * Java Collections Framework. *

* * @param the type of elements held in this collection * * Note: This was copied from Apache Harmony and modified to suit the needs of * Commons Pool. * * @since 2.0 */ class LinkedBlockingDeque extends AbstractQueue implements Deque, Serializable { /* * Implemented as a simple doubly-linked list protected by a * single lock and using conditions to manage blocking. * * To implement weakly consistent iterators, it appears we need to * keep all Nodes GC-reachable from a predecessor dequeued Node. * That would cause two problems: * - allow a rogue Iterator to cause unbounded memory retention * - cause cross-generational linking of old Nodes to new Nodes if * a Node was tenured while live, which generational GCs have a * hard time dealing with, causing repeated major collections. * However, only non-deleted Nodes need to be reachable from * dequeued Nodes, and reachability does not necessarily have to * be of the kind understood by the GC. We use the trick of * linking a Node that has just been dequeued to itself. Such a * self-link implicitly means to jump to "first" (for next links) * or "last" (for prev links). */ /* * We have "diamond" multiple interface/abstract class inheritance * here, and that introduces ambiguities. Often we want the * BlockingDeque javadoc combined with the AbstractQueue * implementation, so a lot of method specs are duplicated here. */ /** * Base class for Iterators for LinkedBlockingDeque */ private abstract class AbstractItr implements Iterator { /** * The next node to return in next() */ Node next; /** * nextItem holds on to item fields because once we claim that * an element exists in hasNext(), we must return item read * under lock (in advance()) even if it was in the process of * being removed when hasNext() was called. */ E nextItem; /** * Node returned by most recent call to next. Needed by remove. * Reset to null if this element is deleted by a call to remove. */ private Node lastRet; /** * Create a new iterator. Sets the initial position. */ AbstractItr() { // set to initial position lock.lock(); try { next = firstNode(); nextItem = next == null ? null : next.item; } finally { lock.unlock(); } } /** * Advances next. */ void advance() { lock.lock(); try { // assert next != null; next = succ(next); nextItem = next == null ? null : next.item; } finally { lock.unlock(); } } /** * Obtain the first node to be returned by the iterator. * * @return first node */ abstract Node firstNode(); @Override public boolean hasNext() { return next != null; } @Override public E next() { if (next == null) { throw new NoSuchElementException(); } lastRet = next; final E x = nextItem; advance(); return x; } /** * For a given node, obtain the next node to be returned by the * iterator. * * @param n given node * * @return next node */ abstract Node nextNode(Node n); @Override public void remove() { final Node n = lastRet; if (n == null) { throw new IllegalStateException(); } lastRet = null; lock.lock(); try { if (n.item != null) { unlink(n); } } finally { lock.unlock(); } } /** * Returns the successor node of the given non-null, but * possibly previously deleted, node. * * @param n node whose successor is sought * @return successor node */ private Node succ(Node n) { // Chains of deleted nodes ending in null or self-links // are possible if multiple interior nodes are removed. for (;;) { final Node s = nextNode(n); if (s == null) { return null; } if (s.item != null) { return s; } if (s == n) { return firstNode(); } n = s; } } } /** Descending iterator */ private class DescendingItr extends AbstractItr { @Override Node firstNode() { return last; } @Override Node nextNode(final Node n) { return n.prev; } } /** Forward iterator */ private class Itr extends AbstractItr { @Override Node firstNode() { return first; } @Override Node nextNode(final Node n) { return n.next; } } /** * Doubly-linked list node class. * * @param node item type */ private static final class Node { /** * The item, or null if this node has been removed. */ E item; /** * One of: * - the real predecessor Node * - this Node, meaning the predecessor is tail * - null, meaning there is no predecessor */ Node prev; /** * One of: * - the real successor Node * - this Node, meaning the successor is head * - null, meaning there is no successor */ Node next; /** * Create a new list node. * * @param x The list item * @param p Previous item * @param n Next item */ Node(final E x, final Node p, final Node n) { item = x; prev = p; next = n; } } private static final long serialVersionUID = -387911632671998426L; /** * Pointer to first node. * Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */ private transient Node first; // @GuardedBy("lock") /** * Pointer to last node. * Invariant: (first == null && last == null) || * (last.next == null && last.item != null) */ private transient Node last; // @GuardedBy("lock") /** Number of items in the deque */ private transient int count; // @GuardedBy("lock") /** Maximum number of items in the deque */ private final int capacity; /** Main lock guarding all access */ private final InterruptibleReentrantLock lock; /** Condition for waiting takes */ private final Condition notEmpty; /** Condition for waiting puts */ private final Condition notFull; /** * Creates a {@code LinkedBlockingDeque} with a capacity of * {@link Integer#MAX_VALUE}. */ public LinkedBlockingDeque() { this(Integer.MAX_VALUE); } /** * Creates a {@code LinkedBlockingDeque} with a capacity of * {@link Integer#MAX_VALUE} and the given fairness policy. * @param fairness true means threads waiting on the deque should be served * as if waiting in a FIFO request queue */ public LinkedBlockingDeque(final boolean fairness) { this(Integer.MAX_VALUE, fairness); } // Basic linking and unlinking operations, called only while holding lock /** * Creates a {@code LinkedBlockingDeque} with a capacity of * {@link Integer#MAX_VALUE}, initially containing the elements of * the given collection, added in traversal order of the * collection's iterator. * * @param c the collection of elements to initially contain * @throws NullPointerException if the specified collection or any * of its elements are null */ public LinkedBlockingDeque(final Collection c) { this(Integer.MAX_VALUE); lock.lock(); // Never contended, but necessary for visibility try { for (final E e : c) { Objects.requireNonNull(e); if (!linkLast(e)) { throw new IllegalStateException("Deque full"); } } } finally { lock.unlock(); } } /** * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity. * * @param capacity the capacity of this deque * @throws IllegalArgumentException if {@code capacity} is less than 1 */ public LinkedBlockingDeque(final int capacity) { this(capacity, false); } /** * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity * and fairness policy. * * @param capacity the capacity of this deque * @param fairness true means threads waiting on the deque should be served * as if waiting in a FIFO request queue * @throws IllegalArgumentException if {@code capacity} is less than 1 */ public LinkedBlockingDeque(final int capacity, final boolean fairness) { if (capacity <= 0) { throw new IllegalArgumentException(); } this.capacity = capacity; lock = new InterruptibleReentrantLock(fairness); notEmpty = lock.newCondition(); notFull = lock.newCondition(); } /** * {@inheritDoc} */ @Override public boolean add(final E e) { addLast(e); return true; } /** * {@inheritDoc} */ @Override public void addFirst(final E e) { if (!offerFirst(e)) { throw new IllegalStateException("Deque full"); } } // BlockingDeque methods /** * {@inheritDoc} */ @Override public void addLast(final E e) { if (!offerLast(e)) { throw new IllegalStateException("Deque full"); } } /** * Atomically removes all of the elements from this deque. * The deque will be empty after this call returns. */ @Override public void clear() { lock.lock(); try { for (Node f = first; f != null;) { f.item = null; final Node n = f.next; f.prev = null; f.next = null; f = n; } first = last = null; count = 0; notFull.signalAll(); } finally { lock.unlock(); } } /** * Returns {@code true} if this deque contains the specified element. * More formally, returns {@code true} if and only if this deque contains * at least one element {@code e} such that {@code o.equals(e)}. * * @param o object to be checked for containment in this deque * @return {@code true} if this deque contains the specified element */ @Override public boolean contains(final Object o) { if (o == null) { return false; } lock.lock(); try { for (Node p = first; p != null; p = p.next) { if (o.equals(p.item)) { return true; } } return false; } finally { lock.unlock(); } } /** * {@inheritDoc} */ @Override public Iterator descendingIterator() { return new DescendingItr(); } /** * Drains the queue to the specified collection. * * @param c The collection to add the elements to * * @return number of elements added to the collection * * @throws UnsupportedOperationException if the add operation is not * supported by the specified collection * @throws ClassCastException if the class of the elements held by this * collection prevents them from being added to the specified * collection * @throws NullPointerException if c is null * @throws IllegalArgumentException if c is this instance */ public int drainTo(final Collection c) { return drainTo(c, Integer.MAX_VALUE); } /** * Drains no more than the specified number of elements from the queue to the * specified collection. * * @param c collection to add the elements to * @param maxElements maximum number of elements to remove from the queue * * @return number of elements added to the collection * @throws UnsupportedOperationException if the add operation is not * supported by the specified collection * @throws ClassCastException if the class of the elements held by this * collection prevents them from being added to the specified * collection * @throws NullPointerException if c is null * @throws IllegalArgumentException if c is this instance */ public int drainTo(final Collection c, final int maxElements) { Objects.requireNonNull(c, "c"); if (c == this) { throw new IllegalArgumentException(); } lock.lock(); try { final int n = Math.min(maxElements, count); for (int i = 0; i < n; i++) { c.add(first.item); // In this order, in case add() throws. unlinkFirst(); } return n; } finally { lock.unlock(); } } /** * Retrieves, but does not remove, the head of the queue represented by * this deque. This method differs from {@link #peek peek} only in that * it throws an exception if this deque is empty. * *

This method is equivalent to {@link #getFirst() getFirst}. * * @return the head of the queue represented by this deque * @throws NoSuchElementException if this deque is empty */ @Override public E element() { return getFirst(); } /** * {@inheritDoc} */ @Override public E getFirst() { final E x = peekFirst(); if (x == null) { throw new NoSuchElementException(); } return x; } /** * {@inheritDoc} */ @Override public E getLast() { final E x = peekLast(); if (x == null) { throw new NoSuchElementException(); } return x; } /** * Gets the length of the queue of threads waiting to take instances from this deque. See disclaimer on accuracy * in {@link java.util.concurrent.locks.ReentrantLock#getWaitQueueLength(Condition)}. * * @return number of threads waiting on this deque's notEmpty condition. */ public int getTakeQueueLength() { lock.lock(); try { return lock.getWaitQueueLength(notEmpty); } finally { lock.unlock(); } } /** * Returns true if there are threads waiting to take instances from this deque. See disclaimer on accuracy in * {@link java.util.concurrent.locks.ReentrantLock#hasWaiters(Condition)}. * * @return true if there is at least one thread waiting on this deque's notEmpty condition. */ public boolean hasTakeWaiters() { lock.lock(); try { return lock.hasWaiters(notEmpty); } finally { lock.unlock(); } } /** * Interrupts the threads currently waiting to take an object from the pool. See disclaimer on accuracy in * {@link java.util.concurrent.locks.ReentrantLock#getWaitingThreads(Condition)}. */ public void interuptTakeWaiters() { lock.lock(); try { lock.interruptWaiters(notEmpty); } finally { lock.unlock(); } } /** * Returns an iterator over the elements in this deque in proper sequence. * The elements will be returned in order from first (head) to last (tail). * The returned {@code Iterator} is a "weakly consistent" iterator that * will never throw {@link java.util.ConcurrentModificationException * ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. * * @return an iterator over the elements in this deque in proper sequence */ @Override public Iterator iterator() { return new Itr(); } /** * Links provided element as first element, or returns false if full. * * @param e The element to link as the first element. * * @return {@code true} if successful, otherwise {@code false} */ private boolean linkFirst(final E e) { // assert lock.isHeldByCurrentThread(); if (count >= capacity) { return false; } final Node f = first; final Node x = new Node<>(e, null, f); first = x; if (last == null) { last = x; } else { f.prev = x; } ++count; notEmpty.signal(); return true; } /** * Links provided element as last element, or returns false if full. * * @param e The element to link as the last element. * * @return {@code true} if successful, otherwise {@code false} */ private boolean linkLast(final E e) { // assert lock.isHeldByCurrentThread(); if (count >= capacity) { return false; } final Node l = last; final Node x = new Node<>(e, l, null); last = x; if (first == null) { first = x; } else { l.next = x; } ++count; notEmpty.signal(); return true; } /** * {@inheritDoc} */ @Override public boolean offer(final E e) { return offerLast(e); } /** * Links the provided element as the last in the queue, waiting up to the * specified time to do so if the queue is full. *

* This method is equivalent to {@link #offerLast(Object, long, TimeUnit)} * * @param e element to link * @param timeout length of time to wait * * @return {@code true} if successful, otherwise {@code false} * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ boolean offer(final E e, final Duration timeout) throws InterruptedException { return offerLast(e, timeout); } /** * Links the provided element as the last in the queue, waiting up to the * specified time to do so if the queue is full. *

* This method is equivalent to {@link #offerLast(Object, long, TimeUnit)} * * @param e element to link * @param timeout length of time to wait * @param unit units that timeout is expressed in * * @return {@code true} if successful, otherwise {@code false} * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ public boolean offer(final E e, final long timeout, final TimeUnit unit) throws InterruptedException { return offerLast(e, timeout, unit); } /** * {@inheritDoc} */ @Override public boolean offerFirst(final E e) { Objects.requireNonNull(e, "e"); lock.lock(); try { return linkFirst(e); } finally { lock.unlock(); } } /** * Links the provided element as the first in the queue, waiting up to the * specified time to do so if the queue is full. * * @param e element to link * @param timeout length of time to wait * * @return {@code true} if successful, otherwise {@code false} * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ public boolean offerFirst(final E e, final Duration timeout) throws InterruptedException { Objects.requireNonNull(e, "e"); long nanos = timeout.toNanos(); lock.lockInterruptibly(); try { while (!linkFirst(e)) { if (nanos <= 0) { return false; } nanos = notFull.awaitNanos(nanos); } return true; } finally { lock.unlock(); } } /** * Links the provided element as the first in the queue, waiting up to the * specified time to do so if the queue is full. * * @param e element to link * @param timeout length of time to wait * @param unit units that timeout is expressed in * * @return {@code true} if successful, otherwise {@code false} * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ public boolean offerFirst(final E e, final long timeout, final TimeUnit unit) throws InterruptedException { return offerFirst(e, PoolImplUtils.toDuration(timeout, unit)); } /** * {@inheritDoc} */ @Override public boolean offerLast(final E e) { Objects.requireNonNull(e, "e"); lock.lock(); try { return linkLast(e); } finally { lock.unlock(); } } /** * Links the provided element as the last in the queue, waiting up to the * specified time to do so if the queue is full. * * @param e element to link * @param timeout length of time to wait * * @return {@code true} if successful, otherwise {@code false} * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whist waiting * for space */ boolean offerLast(final E e, final Duration timeout) throws InterruptedException { Objects.requireNonNull(e, "e"); long nanos = timeout.toNanos(); lock.lockInterruptibly(); try { while (!linkLast(e)) { if (nanos <= 0) { return false; } nanos = notFull.awaitNanos(nanos); } return true; } finally { lock.unlock(); } } /** * Links the provided element as the last in the queue, waiting up to the * specified time to do so if the queue is full. * * @param e element to link * @param timeout length of time to wait * @param unit units that timeout is expressed in * * @return {@code true} if successful, otherwise {@code false} * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whist waiting * for space */ public boolean offerLast(final E e, final long timeout, final TimeUnit unit) throws InterruptedException { return offerLast(e, PoolImplUtils.toDuration(timeout, unit)); } @Override public E peek() { return peekFirst(); } // BlockingQueue methods @Override public E peekFirst() { lock.lock(); try { return first == null ? null : first.item; } finally { lock.unlock(); } } @Override public E peekLast() { lock.lock(); try { return last == null ? null : last.item; } finally { lock.unlock(); } } @Override public E poll() { return pollFirst(); } /** * Unlinks the first element in the queue, waiting up to the specified time * to do so if the queue is empty. * *

This method is equivalent to {@link #pollFirst(long, TimeUnit)}. * * @param timeout length of time to wait * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ E poll(final Duration timeout) throws InterruptedException { return pollFirst(timeout); } /** * Unlinks the first element in the queue, waiting up to the specified time * to do so if the queue is empty. * *

This method is equivalent to {@link #pollFirst(long, TimeUnit)}. * * @param timeout length of time to wait * @param unit units that timeout is expressed in * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ public E poll(final long timeout, final TimeUnit unit) throws InterruptedException { return pollFirst(timeout, unit); } @Override public E pollFirst() { lock.lock(); try { return unlinkFirst(); } finally { lock.unlock(); } } /** * Unlinks the first element in the queue, waiting up to the specified time * to do so if the queue is empty. * * @param timeout length of time to wait * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ E pollFirst(final Duration timeout) throws InterruptedException { long nanos = timeout.toNanos(); lock.lockInterruptibly(); try { E x; while ((x = unlinkFirst()) == null) { if (nanos <= 0) { return null; } nanos = notEmpty.awaitNanos(nanos); } return x; } finally { lock.unlock(); } } /** * Unlinks the first element in the queue, waiting up to the specified time * to do so if the queue is empty. * * @param timeout length of time to wait * @param unit units that timeout is expressed in * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ public E pollFirst(final long timeout, final TimeUnit unit) throws InterruptedException { return pollFirst(PoolImplUtils.toDuration(timeout, unit)); } @Override public E pollLast() { lock.lock(); try { return unlinkLast(); } finally { lock.unlock(); } } /** * Unlinks the last element in the queue, waiting up to the specified time * to do so if the queue is empty. * * @param timeout length of time to wait * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ public E pollLast(final Duration timeout) throws InterruptedException { long nanos = timeout.toNanos(); lock.lockInterruptibly(); try { E x; while ( (x = unlinkLast()) == null) { if (nanos <= 0) { return null; } nanos = notEmpty.awaitNanos(nanos); } return x; } finally { lock.unlock(); } } /** * Unlinks the last element in the queue, waiting up to the specified time * to do so if the queue is empty. * * @param timeout length of time to wait * @param unit units that timeout is expressed in * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ public E pollLast(final long timeout, final TimeUnit unit) throws InterruptedException { return pollLast(PoolImplUtils.toDuration(timeout, unit)); } /** * {@inheritDoc} */ @Override public E pop() { return removeFirst(); } /** * {@inheritDoc} */ @Override public void push(final E e) { addFirst(e); } /** * Links the provided element as the last in the queue, waiting until there * is space to do so if the queue is full. * *

This method is equivalent to {@link #putLast(Object)}. * * @param e element to link * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ public void put(final E e) throws InterruptedException { putLast(e); } /** * Links the provided element as the first in the queue, waiting until there * is space to do so if the queue is full. * * @param e element to link * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ public void putFirst(final E e) throws InterruptedException { Objects.requireNonNull(e, "e"); lock.lock(); try { while (!linkFirst(e)) { notFull.await(); } } finally { lock.unlock(); } } /** * Links the provided element as the last in the queue, waiting until there * is space to do so if the queue is full. * * @param e element to link * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ public void putLast(final E e) throws InterruptedException { Objects.requireNonNull(e, "e"); lock.lock(); try { while (!linkLast(e)) { notFull.await(); } } finally { lock.unlock(); } } // Stack methods /** * Reconstitutes this deque from a stream (that is, * deserialize it). * @param s the stream */ private void readObject(final java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); count = 0; first = null; last = null; // Read in all elements and place in queue for (;;) { @SuppressWarnings("unchecked") final E item = (E)s.readObject(); if (item == null) { break; } add(item); } } /** * Returns the number of additional elements that this deque can ideally * (in the absence of memory or resource constraints) accept without * blocking. This is always equal to the initial capacity of this deque * less the current {@code size} of this deque. * *

Note that you cannot always tell if an attempt to insert * an element will succeed by inspecting {@code remainingCapacity} * because it may be the case that another thread is about to * insert or remove an element. * * @return The number of additional elements the queue is able to accept */ public int remainingCapacity() { lock.lock(); try { return capacity - count; } finally { lock.unlock(); } } // Collection methods /** * Retrieves and removes the head of the queue represented by this deque. * This method differs from {@link #poll poll} only in that it throws an * exception if this deque is empty. * *

This method is equivalent to {@link #removeFirst() removeFirst}. * * @return the head of the queue represented by this deque * @throws NoSuchElementException if this deque is empty */ @Override public E remove() { return removeFirst(); } /** * Removes the first occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the first element {@code e} such that * {@code o.equals(e)} (if such an element exists). * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * *

This method is equivalent to * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}. * * @param o element to be removed from this deque, if present * @return {@code true} if this deque changed as a result of the call */ @Override public boolean remove(final Object o) { return removeFirstOccurrence(o); } /** * {@inheritDoc} */ @Override public E removeFirst() { final E x = pollFirst(); if (x == null) { throw new NoSuchElementException(); } return x; } /* * TODO: Add support for more efficient bulk operations. * * We don't want to acquire the lock for every iteration, but we * also want other threads a chance to interact with the * collection, especially when count is close to capacity. */ // /** // * Adds all of the elements in the specified collection to this // * queue. Attempts to addAll of a queue to itself result in // * {@code IllegalArgumentException}. Further, the behavior of // * this operation is undefined if the specified collection is // * modified while the operation is in progress. // * // * @param c collection containing elements to be added to this queue // * @return {@code true} if this queue changed as a result of the call // * @throws ClassCastException // * @throws NullPointerException // * @throws IllegalArgumentException // * @throws IllegalStateException // * @see #add(Object) // */ // public boolean addAll(Collection c) { // if (c == null) // throw new NullPointerException(); // if (c == this) // throw new IllegalArgumentException(); // final ReentrantLock lock = this.lock; // lock.lock(); // try { // boolean modified = false; // for (E e : c) // if (linkLast(e)) // modified = true; // return modified; // } finally { // lock.unlock(); // } // } @Override public boolean removeFirstOccurrence(final Object o) { if (o == null) { return false; } lock.lock(); try { for (Node p = first; p != null; p = p.next) { if (o.equals(p.item)) { unlink(p); return true; } } return false; } finally { lock.unlock(); } } /** * {@inheritDoc} */ @Override public E removeLast() { final E x = pollLast(); if (x == null) { throw new NoSuchElementException(); } return x; } @Override public boolean removeLastOccurrence(final Object o) { if (o == null) { return false; } lock.lock(); try { for (Node p = last; p != null; p = p.prev) { if (o.equals(p.item)) { unlink(p); return true; } } return false; } finally { lock.unlock(); } } /** * Returns the number of elements in this deque. * * @return the number of elements in this deque */ @Override public int size() { lock.lock(); try { return count; } finally { lock.unlock(); } } /** * Unlinks the first element in the queue, waiting until there is an element * to unlink if the queue is empty. * *

This method is equivalent to {@link #takeFirst()}. * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ public E take() throws InterruptedException { return takeFirst(); } /** * Unlinks the first element in the queue, waiting until there is an element * to unlink if the queue is empty. * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ public E takeFirst() throws InterruptedException { lock.lock(); try { E x; while ( (x = unlinkFirst()) == null) { notEmpty.await(); } return x; } finally { lock.unlock(); } } /** * Unlinks the last element in the queue, waiting until there is an element * to unlink if the queue is empty. * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ public E takeLast() throws InterruptedException { lock.lock(); try { E x; while ( (x = unlinkLast()) == null) { notEmpty.await(); } return x; } finally { lock.unlock(); } } /** * Returns an array containing all of the elements in this deque, in * proper sequence (from first to last element). * *

The returned array will be "safe" in that no references to it are * maintained by this deque. (In other words, this method must allocate * a new array). The caller is thus free to modify the returned array. * *

This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all of the elements in this deque */ @Override public Object[] toArray() { lock.lock(); try { final Object[] a = new Object[count]; int k = 0; for (Node p = first; p != null; p = p.next) { a[k++] = p.item; } return a; } finally { lock.unlock(); } } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public T[] toArray(T[] a) { lock.lock(); try { if (a.length < count) { a = (T[])java.lang.reflect.Array.newInstance (a.getClass().getComponentType(), count); } int k = 0; for (Node p = first; p != null; p = p.next) { a[k++] = (T)p.item; } if (a.length > k) { a[k] = null; } return a; } finally { lock.unlock(); } } @Override public String toString() { lock.lock(); try { return super.toString(); } finally { lock.unlock(); } } /** * Unlinks the provided node. * * @param x The node to unlink */ private void unlink(final Node x) { // assert lock.isHeldByCurrentThread(); final Node p = x.prev; final Node n = x.next; if (p == null) { unlinkFirst(); } else if (n == null) { unlinkLast(); } else { p.next = n; n.prev = p; x.item = null; // Don't mess with x's links. They may still be in use by // an iterator. --count; notFull.signal(); } } // Monitoring methods /** * Removes and returns the first element, or null if empty. * * @return The first element or {@code null} if empty */ private E unlinkFirst() { // assert lock.isHeldByCurrentThread(); final Node f = first; if (f == null) { return null; } final Node n = f.next; final E item = f.item; f.item = null; f.next = f; // help GC first = n; if (n == null) { last = null; } else { n.prev = null; } --count; notFull.signal(); return item; } /** * Removes and returns the last element, or null if empty. * * @return The first element or {@code null} if empty */ private E unlinkLast() { // assert lock.isHeldByCurrentThread(); final Node l = last; if (l == null) { return null; } final Node p = l.prev; final E item = l.item; l.item = null; l.prev = l; // help GC last = p; if (p == null) { first = null; } else { p.next = null; } --count; notFull.signal(); return item; } /** * Saves the state of this deque to a stream (that is, serialize it). * * @serialData The capacity (int), followed by elements (each an * {@code Object}) in the proper order, followed by a null * @param s the stream */ private void writeObject(final java.io.ObjectOutputStream s) throws java.io.IOException { lock.lock(); try { // Write out capacity and any hidden stuff s.defaultWriteObject(); // Write out all elements in the proper order. for (Node p = first; p != null; p = p.next) { s.writeObject(p.item); } // Use trailing null as sentinel s.writeObject(null); } finally { lock.unlock(); } } } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/NoOpCallStack.java000066400000000000000000000030041405425132200330220ustar00rootroot00000000000000/* * 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.commons.pool2.impl; import java.io.PrintWriter; /** * CallStack strategy using no-op implementations of all functionality. Can be used by default when abandoned object * logging is disabled. * * @since 2.5 */ public class NoOpCallStack implements CallStack { /** * Singleton instance. */ public static final CallStack INSTANCE = new NoOpCallStack(); /** * Constructs the singleton instance. */ private NoOpCallStack() { } @Override public void clear() { // no-op } @Override public void fillInStackTrace() { // no-op } @Override public boolean printStackTrace(final PrintWriter writer) { return false; } } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java000066400000000000000000000174051405425132200331530ustar00rootroot00000000000000/* * 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.commons.pool2.impl; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.Objects; import java.util.concurrent.TimeUnit; import org.apache.commons.pool2.PooledObjectFactory; /** * Implementation specific utilities. * * @since 2.0 */ class PoolImplUtils { /** * Identifies the concrete type of object that an object factory creates. * * @param factoryClass * The factory to examine * * @return the type of object the factory creates */ @SuppressWarnings("rawtypes") static Class getFactoryType(final Class factoryClass) { final Class type = PooledObjectFactory.class; final Object genericType = getGenericType(type, factoryClass); if (genericType instanceof Integer) { // POOL-324 org.apache.commons.pool2.impl.GenericObjectPool.getFactoryType() throws // java.lang.ClassCastException // // A bit hackish, but we must handle cases when getGenericType() does not return a concrete types. final ParameterizedType pi = getParameterizedType(type, factoryClass); if (pi != null) { final Type[] bounds = ((TypeVariable) pi.getActualTypeArguments()[((Integer) genericType).intValue()]).getBounds(); if (bounds != null && bounds.length > 0) { final Type bound0 = bounds[0]; if (bound0 instanceof Class) { return (Class) bound0; } } } // last resort: Always return a Class return Object.class; } return (Class) genericType; } /** * Gets the concrete type used by an implementation of an interface that uses a generic type. * * @param type * The interface that defines a generic type * @param clazz * The class that implements the interface with a concrete type * @param * The interface type * * @return concrete type used by the implementation */ private static Object getGenericType(final Class type, final Class clazz) { if (type == null || clazz == null) { // Error will be logged further up the call stack return null; } // Look to see if this class implements the generic interface final ParameterizedType pi = getParameterizedType(type, clazz); if (pi != null) { return getTypeParameter(clazz, pi.getActualTypeArguments()[0]); } // Interface not found on this class. Look at the superclass. @SuppressWarnings("unchecked") final Class superClass = (Class) clazz.getSuperclass(); final Object result = getGenericType(type, superClass); if (result instanceof Class) { // Superclass implements interface and defines explicit type for generic return result; } if (result instanceof Integer) { // Superclass implements interface and defines unknown type for generic // Map that unknown type to the generic types defined in this class final ParameterizedType superClassType = (ParameterizedType) clazz.getGenericSuperclass(); return getTypeParameter(clazz, superClassType.getActualTypeArguments()[((Integer) result).intValue()]); } // Error will be logged further up the call stack return null; } /** * Gets the matching parameterized type or null. * @param type * The interface that defines a generic type. * @param clazz * The class that implements the interface with a concrete type. * @param * The interface type. * @return the matching parameterized type or null. */ private static ParameterizedType getParameterizedType(final Class type, final Class clazz) { for (final Type iface : clazz.getGenericInterfaces()) { // Only need to check interfaces that use generics if (iface instanceof ParameterizedType) { final ParameterizedType pi = (ParameterizedType) iface; // Look for the generic interface if (pi.getRawType() instanceof Class && type.isAssignableFrom((Class) pi.getRawType())) { return pi; } } } return null; } /** * For a generic parameter, return either the Class used or if the type is unknown, the index for the type in * definition of the class * * @param clazz * defining class * @param argType * the type argument of interest * * @return An instance of {@link Class} representing the type used by the type parameter or an instance of * {@link Integer} representing the index for the type in the definition of the defining class */ private static Object getTypeParameter(final Class clazz, final Type argType) { if (argType instanceof Class) { return argType; } final TypeVariable[] tvs = clazz.getTypeParameters(); for (int i = 0; i < tvs.length; i++) { if (tvs[i].equals(argType)) { return Integer.valueOf(i); } } return null; } static boolean isPositive(final Duration delay) { return !delay.isNegative() && !delay.isZero(); } /** * Converts a {@link TimeUnit} to a {@link ChronoUnit}. * * @param timeUnit A TimeUnit. * @return The corresponding ChronoUnit. */ static ChronoUnit toChronoUnit(final TimeUnit timeUnit) { // TODO when using Java >= 9: Use TimeUnit.toChronoUnit(). switch (Objects.requireNonNull(timeUnit)) { case NANOSECONDS: return ChronoUnit.NANOS; case MICROSECONDS: return ChronoUnit.MICROS; case MILLISECONDS: return ChronoUnit.MILLIS; case SECONDS: return ChronoUnit.SECONDS; case MINUTES: return ChronoUnit.MINUTES; case HOURS: return ChronoUnit.HOURS; case DAYS: return ChronoUnit.DAYS; default: throw new IllegalArgumentException(timeUnit.toString()); } } /** * Converts am amount and TimeUnit into a Duration. * * @param amount the amount of the duration, measured in terms of the unit, positive or negative * @param timeUnit the unit that the duration is measured in, must have an exact duration, not null * @return a Duration. */ static Duration toDuration(final long amount, final TimeUnit timeUnit) { return Duration.of(amount, PoolImplUtils.toChronoUnit(timeUnit)); } } PooledSoftReference.java000066400000000000000000000062171405425132200342140ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.lang.ref.SoftReference; /** * Extension of {@link DefaultPooledObject} to wrap pooled soft references. * *

This class is intended to be thread-safe.

* * @param the type of the underlying object that the wrapped SoftReference * refers to. * * @since 2.0 */ public class PooledSoftReference extends DefaultPooledObject { /** SoftReference wrapped by this object */ private volatile SoftReference reference; /** * Creates a new PooledSoftReference wrapping the provided reference. * * @param reference SoftReference to be managed by the pool */ public PooledSoftReference(final SoftReference reference) { super(null); // Null the hard reference in the parent this.reference = reference; } /** * Gets the object that the wrapped SoftReference refers to. *

* Note that if the reference has been cleared, this method will return * null. * * @return Object referred to by the SoftReference */ @Override public T getObject() { return reference.get(); } /** * Gets the SoftReference wrapped by this object. * * @return underlying SoftReference */ public synchronized SoftReference getReference() { return reference; } /** * Sets the wrapped reference. * *

This method exists to allow a new, non-registered reference to be * held by the pool to track objects that have been checked out of the pool. * The actual parameter should be a reference to the same * object that {@link #getObject()} returns before calling this method.

* * @param reference new reference */ public synchronized void setReference(final SoftReference reference) { this.reference = reference; } /** * {@inheritDoc} */ @Override public String toString() { final StringBuilder result = new StringBuilder(); result.append("Referenced Object: "); result.append(getObject().toString()); result.append(", State: "); synchronized (this) { result.append(getState().toString()); } return result.toString(); // TODO add other attributes // TODO encapsulate state and other attribute display in parent } } SecurityManagerCallStack.java000066400000000000000000000102421405425132200351740ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.security.AccessController; import java.security.PrivilegedAction; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; /** * CallStack strategy using a {@link SecurityManager}. Obtaining the current call stack is much faster via a * SecurityManger, but access to the underlying method may be restricted by the current SecurityManager. In environments * where a SecurityManager cannot be created, {@link ThrowableCallStack} should be used instead. * * @see RuntimePermission * @see SecurityManager#getClassContext() * @since 2.4.3 */ public class SecurityManagerCallStack implements CallStack { /** * A custom security manager. */ private static class PrivateSecurityManager extends SecurityManager { /** * Get the class stack. * * @return class stack */ private List>> getCallStack() { final Class[] classes = getClassContext(); final List>> stack = new ArrayList<>(classes.length); for (final Class klass : classes) { stack.add(new WeakReference<>(klass)); } return stack; } } /** * A snapshot of a class stack. */ private static class Snapshot { private final long timestampMillis = System.currentTimeMillis(); private final List>> stack; /** * Create a new snapshot with a class stack. * * @param stack class stack */ private Snapshot(final List>> stack) { this.stack = stack; } } private final String messageFormat; //@GuardedBy("dateFormat") private final DateFormat dateFormat; private final PrivateSecurityManager securityManager; private volatile Snapshot snapshot; /** * Create a new instance. * * @param messageFormat message format * @param useTimestamp whether to format the dates in the output message or not */ public SecurityManagerCallStack(final String messageFormat, final boolean useTimestamp) { this.messageFormat = messageFormat; this.dateFormat = useTimestamp ? new SimpleDateFormat(messageFormat) : null; this.securityManager = AccessController.doPrivileged((PrivilegedAction) PrivateSecurityManager::new); } @Override public void clear() { snapshot = null; } @Override public void fillInStackTrace() { snapshot = new Snapshot(securityManager.getCallStack()); } @Override public boolean printStackTrace(final PrintWriter writer) { final Snapshot snapshotRef = this.snapshot; if (snapshotRef == null) { return false; } final String message; if (dateFormat == null) { message = messageFormat; } else { synchronized (dateFormat) { message = dateFormat.format(Long.valueOf(snapshotRef.timestampMillis)); } } writer.println(message); for (final WeakReference> reference : snapshotRef.stack) { writer.println(reference.get()); } return true; } } SoftReferenceObjectPool.java000066400000000000000000000377141405425132200350400ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.Iterator; import java.util.NoSuchElementException; import org.apache.commons.pool2.BaseObjectPool; import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.PoolUtils; import org.apache.commons.pool2.PooledObjectFactory; /** * A {@link java.lang.ref.SoftReference SoftReference} based {@link ObjectPool}. *

* This class is intended to be thread-safe. *

* * @param * Type of element pooled in this pool. * * @since 2.0 */ public class SoftReferenceObjectPool extends BaseObjectPool { /** Factory to source pooled objects */ private final PooledObjectFactory factory; /** * Queue of broken references that might be able to be removed from * {@code _pool}. This is used to help {@link #getNumIdle()} be more * accurate with minimal performance overhead. */ private final ReferenceQueue refQueue = new ReferenceQueue<>(); /** Count of instances that have been checkout out to pool clients */ private int numActive; // @GuardedBy("this") /** Total number of instances that have been destroyed */ private long destroyCount; // @GuardedBy("this") /** Total number of instances that have been created */ private long createCount; // @GuardedBy("this") /** Idle references - waiting to be borrowed */ private final LinkedBlockingDeque> idleReferences = new LinkedBlockingDeque<>(); /** All references - checked out or waiting to be borrowed. */ private final ArrayList> allReferences = new ArrayList<>(); /** * Create a {@code SoftReferenceObjectPool} with the specified factory. * * @param factory object factory to use. */ public SoftReferenceObjectPool(final PooledObjectFactory factory) { this.factory = factory; } /** * Creates an object, and places it into the pool. addObject() is useful for * "pre-loading" a pool with idle objects. *

* Before being added to the pool, the newly created instance is * {@link PooledObjectFactory#validateObject( * org.apache.commons.pool2.PooledObject) validated} and * {@link PooledObjectFactory#passivateObject( * org.apache.commons.pool2.PooledObject) passivated}. If * validation fails, the new instance is * {@link PooledObjectFactory#destroyObject( * org.apache.commons.pool2.PooledObject) destroyed}. Exceptions * generated by the factory {@code makeObject} or * {@code passivate} are propagated to the caller. Exceptions * destroying instances are silently swallowed. * * @throws IllegalStateException * if invoked on a {@link #close() closed} pool * @throws Exception * when the {@link #getFactory() factory} has a problem creating * or passivating an object. */ @Override public synchronized void addObject() throws Exception { assertOpen(); if (factory == null) { throw new IllegalStateException( "Cannot add objects without a factory."); } final T obj = factory.makeObject().getObject(); createCount++; // Create and register with the queue final PooledSoftReference ref = new PooledSoftReference<>( new SoftReference<>(obj, refQueue)); allReferences.add(ref); boolean success = true; if (!factory.validateObject(ref)) { success = false; } else { factory.passivateObject(ref); } final boolean shouldDestroy = !success; if (success) { idleReferences.add(ref); notifyAll(); // numActive has changed } if (shouldDestroy) { try { destroy(ref); } catch (final Exception e) { // ignored } } } /** * Borrows an object from the pool. If there are no idle instances available * in the pool, the configured factory's * {@link PooledObjectFactory#makeObject()} method is invoked to create a * new instance. *

* All instances are {@link PooledObjectFactory#activateObject( * org.apache.commons.pool2.PooledObject) activated} * and {@link PooledObjectFactory#validateObject( * org.apache.commons.pool2.PooledObject) * validated} before being returned by this method. If validation fails or * an exception occurs activating or validating an idle instance, the * failing instance is {@link PooledObjectFactory#destroyObject( * org.apache.commons.pool2.PooledObject) * destroyed} and another instance is retrieved from the pool, validated and * activated. This process continues until either the pool is empty or an * instance passes validation. If the pool is empty on activation or it does * not contain any valid instances, the factory's {@code makeObject} * method is used to create a new instance. If the created instance either * raises an exception on activation or fails validation, * {@code NoSuchElementException} is thrown. Exceptions thrown by * {@code MakeObject} are propagated to the caller; but other than * {@code ThreadDeath} or {@code VirtualMachineError}, exceptions * generated by activation, validation or destroy methods are swallowed * silently. * * @throws NoSuchElementException * if a valid object cannot be provided * @throws IllegalStateException * if invoked on a {@link #close() closed} pool * @throws Exception * if an exception occurs creating a new instance * @return a valid, activated object instance */ @SuppressWarnings("null") // ref cannot be null @Override public synchronized T borrowObject() throws Exception { assertOpen(); T obj = null; boolean newlyCreated = false; PooledSoftReference ref = null; while (null == obj) { if (idleReferences.isEmpty()) { if (null == factory) { throw new NoSuchElementException(); } newlyCreated = true; obj = factory.makeObject().getObject(); createCount++; // Do not register with the queue ref = new PooledSoftReference<>(new SoftReference<>(obj)); allReferences.add(ref); } else { ref = idleReferences.pollFirst(); obj = ref.getObject(); // Clear the reference so it will not be queued, but replace with a // a new, non-registered reference so we can still track this object // in allReferences ref.getReference().clear(); ref.setReference(new SoftReference<>(obj)); } if (null != factory && null != obj) { try { factory.activateObject(ref); if (!factory.validateObject(ref)) { throw new Exception("ValidateObject failed"); } } catch (final Throwable t) { PoolUtils.checkRethrow(t); try { destroy(ref); } catch (final Throwable t2) { PoolUtils.checkRethrow(t2); // Swallowed } finally { obj = null; } if (newlyCreated) { throw new NoSuchElementException( "Could not create a validated object, cause: " + t.getMessage()); } } } } numActive++; ref.allocate(); return obj; } /** * Clears any objects sitting idle in the pool. */ @Override public synchronized void clear() { if (null != factory) { final Iterator> iter = idleReferences.iterator(); while (iter.hasNext()) { try { final PooledSoftReference ref = iter.next(); if (null != ref.getObject()) { factory.destroyObject(ref); } } catch (final Exception e) { // ignore error, keep destroying the rest } } } idleReferences.clear(); pruneClearedReferences(); } /** * Closes this pool, and frees any resources associated with it. Invokes * {@link #clear()} to destroy and remove instances in the pool. *

* Calling {@link #addObject} or {@link #borrowObject} after invoking this * method on a pool will cause them to throw an * {@link IllegalStateException}. */ @Override public void close() { super.close(); clear(); } /** * Destroys a {@code PooledSoftReference} and removes it from the idle and all * references pools. * * @param toDestroy PooledSoftReference to destroy * * @throws Exception If an error occurs while trying to destroy the object */ private void destroy(final PooledSoftReference toDestroy) throws Exception { toDestroy.invalidate(); idleReferences.remove(toDestroy); allReferences.remove(toDestroy); try { factory.destroyObject(toDestroy); } finally { destroyCount++; toDestroy.getReference().clear(); } } /** * Finds the PooledSoftReference in allReferences that points to obj. * * @param obj returning object * @return PooledSoftReference wrapping a soft reference to obj */ private PooledSoftReference findReference(final T obj) { final Iterator> iterator = allReferences.iterator(); while (iterator.hasNext()) { final PooledSoftReference reference = iterator.next(); if (reference.getObject() != null && reference.getObject().equals(obj)) { return reference; } } return null; } /** * Gets the {@link PooledObjectFactory} used by this pool to create and * manage object instances. * * @return the factory */ public synchronized PooledObjectFactory getFactory() { return factory; } /** * Gets the number of instances currently borrowed from this pool. * * @return the number of instances currently borrowed from this pool */ @Override public synchronized int getNumActive() { return numActive; } /** * Gets an approximation not less than the of the number of idle * instances in the pool. * * @return estimated number of idle instances in the pool */ @Override public synchronized int getNumIdle() { pruneClearedReferences(); return idleReferences.size(); } /** * {@inheritDoc} */ @Override public synchronized void invalidateObject(final T obj) throws Exception { final PooledSoftReference ref = findReference(obj); if (ref == null) { throw new IllegalStateException( "Object to invalidate is not currently part of this pool"); } if (factory != null) { destroy(ref); } numActive--; notifyAll(); // numActive has changed } /** * If any idle objects were garbage collected, remove their * {@link Reference} wrappers from the idle object pool. */ private void pruneClearedReferences() { // Remove wrappers for enqueued references from idle and allReferences lists removeClearedReferences(idleReferences.iterator()); removeClearedReferences(allReferences.iterator()); while (refQueue.poll() != null) { // empty } } /** * Clears cleared references from iterator's collection * @param iterator iterator over idle/allReferences */ private void removeClearedReferences(final Iterator> iterator) { PooledSoftReference ref; while (iterator.hasNext()) { ref = iterator.next(); if (ref.getReference() == null || ref.getReference().isEnqueued()) { iterator.remove(); } } } /** * Returns an instance to the pool after successful validation and * passivation. The returning instance is destroyed if any of the following * are true: *

    *
  • the pool is closed
  • *
  • {@link PooledObjectFactory#validateObject( * org.apache.commons.pool2.PooledObject) validation} fails *
  • *
  • {@link PooledObjectFactory#passivateObject( * org.apache.commons.pool2.PooledObject) passivation} * throws an exception
  • *
* Exceptions passivating or destroying instances are silently swallowed. * Exceptions validating instances are propagated to the client. * * @param obj * instance to return to the pool * @throws IllegalArgumentException * if obj is not currently part of this pool */ @Override public synchronized void returnObject(final T obj) throws Exception { boolean success = !isClosed(); final PooledSoftReference ref = findReference(obj); if (ref == null) { throw new IllegalStateException( "Returned object not currently part of this pool"); } if (factory != null) { if (!factory.validateObject(ref)) { success = false; } else { try { factory.passivateObject(ref); } catch (final Exception e) { success = false; } } } final boolean shouldDestroy = !success; numActive--; if (success) { // Deallocate and add to the idle instance pool ref.deallocate(); idleReferences.add(ref); } notifyAll(); // numActive has changed if (shouldDestroy && factory != null) { try { destroy(ref); } catch (final Exception e) { // ignored } } } @Override protected void toStringAppendFields(final StringBuilder builder) { super.toStringAppendFields(builder); builder.append(", factory="); builder.append(factory); builder.append(", refQueue="); builder.append(refQueue); builder.append(", numActive="); builder.append(numActive); builder.append(", destroyCount="); builder.append(destroyCount); builder.append(", createCount="); builder.append(createCount); builder.append(", idleReferences="); builder.append(idleReferences); builder.append(", allReferences="); builder.append(allReferences); } } ThrowableCallStack.java000066400000000000000000000054451405425132200340320ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.io.PrintWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; /** * CallStack strategy that uses the stack trace from a {@link Throwable}. This strategy, while slower than the * SecurityManager implementation, provides call stack method names and other metadata in addition to the call stack * of classes. * * @see Throwable#fillInStackTrace() * @since 2.4.3 */ public class ThrowableCallStack implements CallStack { /** * A snapshot of a throwable. */ private static class Snapshot extends Throwable { private static final long serialVersionUID = 1L; private final long timestampMillis = System.currentTimeMillis(); } private final String messageFormat; //@GuardedBy("dateFormat") private final DateFormat dateFormat; private volatile Snapshot snapshot; /** * Create a new instance. * * @param messageFormat message format * @param useTimestamp whether to format the dates in the output message or not */ public ThrowableCallStack(final String messageFormat, final boolean useTimestamp) { this.messageFormat = messageFormat; this.dateFormat = useTimestamp ? new SimpleDateFormat(messageFormat) : null; } @Override public void clear() { snapshot = null; } @Override public void fillInStackTrace() { snapshot = new Snapshot(); } @Override public synchronized boolean printStackTrace(final PrintWriter writer) { final Snapshot snapshotRef = this.snapshot; if (snapshotRef == null) { return false; } final String message; if (dateFormat == null) { message = messageFormat; } else { synchronized (dateFormat) { message = dateFormat.format(Long.valueOf(snapshotRef.timestampMillis)); } } writer.println(message); snapshotRef.printStackTrace(writer); return true; } } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/impl/package.html000066400000000000000000000033111405425132200320040ustar00rootroot00000000000000 Package Documentation for org.apache.commons.pool2.impl

Object pooling API implementations.

{@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool} ({@link org.apache.commons.pool2.impl.GenericKeyedObjectPool GenericKeyedObjectPool}) provides a more robust (but also more complicated) implementation of {@link org.apache.commons.pool2.ObjectPool ObjectPool} ({@link org.apache.commons.pool2.KeyedObjectPool KeyedObjectPool}).

{@link org.apache.commons.pool2.impl.SoftReferenceObjectPool SoftReferenceObjectPool} provides a {@link java.lang.ref.SoftReference SoftReference} based {@link org.apache.commons.pool2.ObjectPool ObjectPool}.

See also the {@link org.apache.commons.pool2} package.

commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/overview.html000066400000000000000000000042621405425132200313240ustar00rootroot00000000000000 Overview of the org.apache.commons.pool2 component

Generic Object pooling API with several implementations.

The org.apache.commons.pool2 package defines a simple interface for a pool of object instances, and a handful of base classes that may be useful when creating pool implementations. The API supports pooling of unique objects which can be requested via a key as well as pools where all objects are equivalent.

The org.apache.commons.pool2.impl package contains several pool implementations. {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool} has many configuration options and can support a limited set of objects such as would be useful in a database connection pool. {@link org.apache.commons.pool2.impl.SoftReferenceObjectPool SoftReferenceObjectPool} has no limit on the number of objects in the pool, but the garbage collector can remove idle objects from the pool as needed. There is also a keyed version of {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool}, {@link org.apache.commons.pool2.impl.GenericKeyedObjectPool GenericKeyedObjectPool}

commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/package.html000066400000000000000000000050271405425132200310510ustar00rootroot00000000000000 Package Documentation for org.apache.commons.pool2

Object pooling API.

The org.apache.commons.pool2 package defines a simple interface for a pool of object instances, and a handful of base classes that may be useful when creating pool implementations.

The pool package itself doesn't define a specific object pooling implementation, but rather a contract that implementations may support in order to be fully interchangeable.

The pool package separates the way in which instances are pooled from the way in which they are created, resulting in a pair of interfaces:

{@link org.apache.commons.pool2.ObjectPool ObjectPool}
defines a simple object pooling interface, with methods for borrowing instances from and returning them to the pool.
{@link org.apache.commons.pool2.PooledObjectFactory PooledObjectFactory}
defines lifecycle methods for object instances contained within a pool. By associating a factory with a pool, the pool can create new object instances as needed.

The pool package also provides a keyed pool interface, which pools instances of multiple types, accessed according to an arbitrary key. See {@link org.apache.commons.pool2.KeyedObjectPool KeyedObjectPool} and {@link org.apache.commons.pool2.KeyedPooledObjectFactory KeyedPooledObjectFactory}.

commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/proxy/000077500000000000000000000000001405425132200277455ustar00rootroot00000000000000BaseProxyHandler.java000066400000000000000000000072071405425132200337510ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; import java.lang.reflect.Method; import org.apache.commons.pool2.UsageTracking; /** * Base implementation for object wrappers when using a * {@link ProxiedObjectPool}. * * @param type of the wrapped pooled object * * @since 2.0 */ class BaseProxyHandler { private volatile T pooledObject; private final UsageTracking usageTracking; /** * Create a new wrapper for the given pooled object. * * @param pooledObject The object to wrap * @param usageTracking The instance, if any (usually the object pool) to * be provided with usage tracking information for this * wrapped object */ BaseProxyHandler(final T pooledObject, final UsageTracking usageTracking) { this.pooledObject = pooledObject; this.usageTracking = usageTracking; } /** * Disable the proxy wrapper. Called when the object has been returned to * the pool. Further use of the wrapper should result in an * {@link IllegalStateException}. * * @return the object that this proxy was wrapping */ T disableProxy() { final T result = pooledObject; pooledObject = null; return result; } /** * Invoke the given method on the wrapped object. * * @param method The method to invoke * @param args The arguments to the method * @return The result of the method call * @throws Throwable If the method invocation fails */ Object doInvoke(final Method method, final Object[] args) throws Throwable { validateProxiedObject(); final T object = getPooledObject(); if (usageTracking != null) { usageTracking.use(object); } return method.invoke(object, args); } /** * Obtain the wrapped, pooled object. * * @return the underlying pooled object */ T getPooledObject() { return pooledObject; } /** * @since 2.4.3 */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append(getClass().getName()); builder.append(" [pooledObject="); builder.append(pooledObject); builder.append(", usageTracking="); builder.append(usageTracking); builder.append("]"); return builder.toString(); } /** * Check that the proxy is still valid (i.e. that {@link #disableProxy()} * has not been called). * * @throws IllegalStateException if {@link #disableProxy()} has been called */ void validateProxiedObject() { if (pooledObject == null) { throw new IllegalStateException("This object may no longer be " + "used as it has been returned to the Object Pool."); } } } CglibProxyHandler.java000066400000000000000000000035401405425132200341130ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; import java.lang.reflect.Method; import org.apache.commons.pool2.UsageTracking; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * CGLib implementation of the proxy handler. * * @param type of the wrapped pooled object * * @since 2.0 */ class CglibProxyHandler extends BaseProxyHandler implements MethodInterceptor { /** * Create a CGLib proxy instance. * * @param pooledObject The object to wrap * @param usageTracking The instance, if any (usually the object pool) to * be provided with usage tracking information for this * wrapped object */ CglibProxyHandler(final T pooledObject, final UsageTracking usageTracking) { super(pooledObject, usageTracking); } @Override public Object intercept(final Object object, final Method method, final Object[] args, final MethodProxy methodProxy) throws Throwable { return doInvoke(method, args); } } CglibProxySource.java000066400000000000000000000047251405425132200340040ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; import org.apache.commons.pool2.UsageTracking; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.Factory; /** * Provides proxy objects using CGLib. * * @param type of the pooled object to be proxied * * @since 2.0 */ public class CglibProxySource implements ProxySource { private final Class superclass; /** * Create a new proxy source for the given class. * * @param superclass The class to proxy */ public CglibProxySource(final Class superclass) { this.superclass = superclass; } @Override public T createProxy(final T pooledObject, final UsageTracking usageTracking) { final Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(superclass); final CglibProxyHandler proxyInterceptor = new CglibProxyHandler<>(pooledObject, usageTracking); enhancer.setCallback(proxyInterceptor); @SuppressWarnings("unchecked") final T proxy = (T) enhancer.create(); return proxy; } @Override public T resolveProxy(final T proxy) { @SuppressWarnings("unchecked") final CglibProxyHandler cglibProxyHandler = (CglibProxyHandler) ((Factory) proxy).getCallback(0); return cglibProxyHandler.disableProxy(); } /** * @since 2.4.3 */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("CglibProxySource [superclass="); builder.append(superclass); builder.append("]"); return builder.toString(); } } JdkProxyHandler.java000066400000000000000000000034451405425132200336070ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import org.apache.commons.pool2.UsageTracking; /** * Java reflection implementation of the proxy handler. * * @param type of the wrapped pooled object * * @since 2.0 */ class JdkProxyHandler extends BaseProxyHandler implements InvocationHandler { /** * Create a Java reflection proxy instance. * * @param pooledObject The object to wrap * @param usageTracking The instance, if any (usually the object pool) to * be provided with usage tracking information for this * wrapped object */ JdkProxyHandler(final T pooledObject, final UsageTracking usageTracking) { super(pooledObject, usageTracking); } @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { return doInvoke(method, args); } } JdkProxySource.java000066400000000000000000000052621405425132200334710ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; import java.lang.reflect.Proxy; import java.util.Arrays; import org.apache.commons.pool2.UsageTracking; /** * Provides proxy objects using Java reflection. * * @param type of the pooled object to be proxied * * @since 2.0 */ public class JdkProxySource implements ProxySource { private final ClassLoader classLoader; private final Class[] interfaces; /** * Create a new proxy source for the given interfaces. * * @param classLoader The class loader with which to create the proxy * @param interfaces The interfaces to proxy */ public JdkProxySource(final ClassLoader classLoader, final Class[] interfaces) { this.classLoader = classLoader; // Defensive copy this.interfaces = new Class[interfaces.length]; System.arraycopy(interfaces, 0, this.interfaces, 0, interfaces.length); } @Override public T createProxy(final T pooledObject, final UsageTracking usageTracking) { @SuppressWarnings("unchecked") final T proxy = (T) Proxy.newProxyInstance(classLoader, interfaces, new JdkProxyHandler<>(pooledObject, usageTracking)); return proxy; } @Override public T resolveProxy(final T proxy) { @SuppressWarnings("unchecked") final JdkProxyHandler jdkProxyHandler = (JdkProxyHandler) Proxy.getInvocationHandler(proxy); return jdkProxyHandler.disableProxy(); } /** * @since 2.4.3 */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("JdkProxySource [classLoader="); builder.append(classLoader); builder.append(", interfaces="); builder.append(Arrays.toString(interfaces)); builder.append("]"); return builder.toString(); } } ProxiedKeyedObjectPool.java000066400000000000000000000076271405425132200351220ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; import java.util.NoSuchElementException; import org.apache.commons.pool2.KeyedObjectPool; import org.apache.commons.pool2.UsageTracking; /** * Create a new keyed object pool where the pooled objects are wrapped in * proxies allowing better control of pooled objects and in particular the * prevention of the continued use of an object by a client after that client * returns the object to the pool. * * @param type of the key * @param type of the pooled object * * @since 2.0 */ public class ProxiedKeyedObjectPool implements KeyedObjectPool { private final KeyedObjectPool pool; private final ProxySource proxySource; /** * Create a new proxied object pool. * * @param pool The object pool to wrap * @param proxySource The source of the proxy objects */ public ProxiedKeyedObjectPool(final KeyedObjectPool pool, final ProxySource proxySource) { this.pool = pool; this.proxySource = proxySource; } @Override public void addObject(final K key) throws Exception, IllegalStateException, UnsupportedOperationException { pool.addObject(key); } @SuppressWarnings("unchecked") @Override public V borrowObject(final K key) throws Exception, NoSuchElementException, IllegalStateException { UsageTracking usageTracking = null; if (pool instanceof UsageTracking) { usageTracking = (UsageTracking) pool; } final V pooledObject = pool.borrowObject(key); return proxySource.createProxy(pooledObject, usageTracking); } @Override public void clear() throws Exception, UnsupportedOperationException { pool.clear(); } @Override public void clear(final K key) throws Exception, UnsupportedOperationException { pool.clear(key); } @Override public void close() { pool.close(); } @Override public int getNumActive() { return pool.getNumActive(); } @Override public int getNumActive(final K key) { return pool.getNumActive(key); } @Override public int getNumIdle() { return pool.getNumIdle(); } @Override public int getNumIdle(final K key) { return pool.getNumIdle(key); } @Override public void invalidateObject(final K key, final V proxy) throws Exception { final V pooledObject = proxySource.resolveProxy(proxy); pool.invalidateObject(key, pooledObject); } @Override public void returnObject(final K key, final V proxy) throws Exception { final V pooledObject = proxySource.resolveProxy(proxy); pool.returnObject(key, pooledObject); } /** * @since 2.4.3 */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("ProxiedKeyedObjectPool [pool="); builder.append(pool); builder.append(", proxySource="); builder.append(proxySource); builder.append("]"); return builder.toString(); } } ProxiedObjectPool.java000066400000000000000000000070041405425132200341250ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; import java.util.NoSuchElementException; import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.UsageTracking; /** * Create a new object pool where the pooled objects are wrapped in proxies * allowing better control of pooled objects and in particular the prevention * of the continued use of an object by a client after that client returns the * object to the pool. * * @param type of the pooled object * * @since 2.0 */ public class ProxiedObjectPool implements ObjectPool { private final ObjectPool pool; private final ProxySource proxySource; /** * Create a new proxied object pool. * * @param pool The object pool to wrap * @param proxySource The source of the proxy objects */ public ProxiedObjectPool(final ObjectPool pool, final ProxySource proxySource) { this.pool = pool; this.proxySource = proxySource; } // --------------------------------------------------- ObjectPool methods @Override public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException { pool.addObject(); } @SuppressWarnings("unchecked") @Override public T borrowObject() throws Exception, NoSuchElementException, IllegalStateException { UsageTracking usageTracking = null; if (pool instanceof UsageTracking) { usageTracking = (UsageTracking) pool; } final T pooledObject = pool.borrowObject(); return proxySource.createProxy(pooledObject, usageTracking); } @Override public void clear() throws Exception, UnsupportedOperationException { pool.clear(); } @Override public void close() { pool.close(); } @Override public int getNumActive() { return pool.getNumActive(); } @Override public int getNumIdle() { return pool.getNumIdle(); } @Override public void invalidateObject(final T proxy) throws Exception { final T pooledObject = proxySource.resolveProxy(proxy); pool.invalidateObject(pooledObject); } @Override public void returnObject(final T proxy) throws Exception { final T pooledObject = proxySource.resolveProxy(proxy); pool.returnObject(pooledObject); } /** * @since 2.4.3 */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("ProxiedObjectPool [pool="); builder.append(pool); builder.append(", proxySource="); builder.append(proxySource); builder.append("]"); return builder.toString(); } } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/proxy/ProxySource.java000066400000000000000000000034201405425132200331110ustar00rootroot00000000000000/* * 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.commons.pool2.proxy; import org.apache.commons.pool2.UsageTracking; /** * The interface that any provider of proxy instances must implement to allow the * {@link ProxiedObjectPool} to create proxies as required. * * @param type of the pooled object to be proxied * * @since 2.0 */ interface ProxySource { /** * Create a new proxy object, wrapping the given pooled object. * * @param pooledObject The object to wrap * @param usageTracking The instance, if any (usually the object pool) to * be provided with usage tracking information for this * wrapped object * * @return the new proxy object */ T createProxy(T pooledObject, UsageTracking usageTracking); /** * Obtain the wrapped object from the given proxy. * * @param proxy The proxy object * * @return The pooled object wrapped by the given proxy */ T resolveProxy(T proxy); } commons-pool-rel-commons-pool-2.10.0/src/main/java/org/apache/commons/pool2/proxy/package.html000066400000000000000000000030261405425132200322270ustar00rootroot00000000000000 Package Documentation for org.apache.commons.pool2.proxy

Object pooling proxy implementation.

The org.apache.commons.pool2.proxy package defines a object pool that wraps all objects returned to clients. This allows it to disable those proxies when the objects are returned thereby enabling the continued use of those objects by clients to be detected..

Support is provided for java.lang.reflect.Proxy and for net.sf.cglib.proxy based proxies. The latter, requires the additional of the optional Code Generation Library (GCLib).

commons-pool-rel-commons-pool-2.10.0/src/site/000077500000000000000000000000001405425132200211455ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/site/resources/000077500000000000000000000000001405425132200231575ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/site/resources/download_pool.cgi000077500000000000000000000021141405425132200265040ustar00rootroot00000000000000#!/bin/sh # ----------------------------------------------------------------------------- # 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. # ----------------------------------------------------------------------------- # # Just call the standard mirrors.cgi script. It will use download.html # as the input template. exec /www/www.apache.org/dyn/mirrors/mirrors.cgi $*commons-pool-rel-commons-pool-2.10.0/src/site/resources/images/000077500000000000000000000000001405425132200244245ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/site/resources/images/pool-logo-white.png000066400000000000000000000251341405425132200301640ustar00rootroot00000000000000‰PNG  IHDRæQÆ¥ÚvsRGB®ÎégAMA± üa pHYsÄÄ•+bKGDÿÿÿ ½§“tIMEÔ^ňV)ÌIDATx^í,UÙþP± Uæ¥UL˜1+˜# ˜&(2–Y´DD̉"Jôš ňDA®"˜E³ûŸ_ßùÍ>óîéÙ]¼|õÕ÷Ÿ§êÙsúœ7ÓçíîíéžYknˆÁ2âËTÀZk­5ªMÇÿVê,ÕGÝizÓú–kKþºÄ܇´Õ²[ý/7~±”˜ÿ›¹é³¿Ô¹º.ãš6wKNÌï}ï{cŠõ×_°ñÆþð‡î|ç;ZW³ œ¶[ƒ¥ Û¿þõ¯ŸýìgG­«}ˆ/Ñ7©Ó|€ßüæ7ƒ+V 6Ùd“ΦÐÞi§ÖÅA ã–[n9ởûÜçßÿþ÷»1uñÓ7Í5Þ7q0ï{ßûF-“c§}âë¹Ï}î‚ýQÁ<kεºë­·Þ’â­±æ6öGcØÖOŸ‘öª/àÜ`Ë}©Mבû°/íƒ>j™¨ó °ÉzrÎ_ÿú×wûËXªiÛãúPy*öÛo¿¹ádâa*‡ƒžûÅ/~1÷÷¿ÿ½—ÿøÇ?:þóŸÿœû׿þ5÷ïÿ»ãpKòÌÎ;ï¼l?ð裞NàØÛ´#ƒì)§œ27L¾ IúV­Z5÷׿þu÷ÙgŸ©ñ«û·¿ýmBm˜qÏ…^ØÍÉp±m«ñî»ï¾S}î´ÓN üÁ .¸ ëkéHue+ÖŒ7÷å©§ž:÷4²f¾óït¶Ò_úH?påÊ•s»ì²ËÄØ:ê¨ñ¼üò—¿ìÖHúI¢wøá‡/˜—ê;}2¾]wÝuÂk æXk(ãO›9g9oð?ÿùOGЛ˜Ã£ÝÄà ŒIqH±_~úÓŸž»æškðÚk¯O€yä‘>˜pûôãã½ï}\̉€n+FˆoãXl¡Ê6Úhî/ùËÜÕW_ÝñÛßþöÜð(Ù”­DÝJã>ñÄ»8úÆÊœL;€$÷Þ{ï·ñ²Ý’kqÇwìµîKö“\¤Ô9ø¹^cÎ[±o¾ùæcÛIýqÄÝ~l­è¾<ùä“{e*;ì°ñ¼Àiã3fl³>MXÇÅÁ¥Ž }ìhK{5ùû’³™˜ÇsÌØ|ÑEMuÌbÍ€à!‡2wÕUWùç?ÿybðèq–IL”´ïd±óR>ç9ÏéõÃÂBg‹-¶X 9jþüç?ÇOâì°Ãs‡zhÿöÛo¿@î¹çžsW^yåÜA4n{ö³Ÿ=wðÁwzê¶Ý?þñüÓŸþÔÅNÄÛšOÈ|³v‘Ûk¯½Æ>ö°‡-ÐgŸ}v/1²þvÛm7/óXõ º­Xg÷eM’(× ‹1÷'I‘~ q{î¹Íýɼ,¶/9PSw?:¶¾}Aó"ëø\«éÓ³{߸ÒúÄîzÔžsf‚2OäV&h313)™lPÌ@0Ž#ÀâP^ÞéNwš»âŠ+æ.¿üò®„ Þ³øSÞK vÌ„®‹+I›>ôÃ$«‡Ö™ÂDgAwÜqŽ6ˆžyæ™ ôÛÐÕŸùÌgÎýä'?™»ôÒKǼì²Ëæþð‡?ÌqÆlÕMYˆ¬ó”IšzÐxYhçœsN7Îï»Þõ®zÛn»m'õw¾ó¾xàMÝ–<>ãóÎ;obA²Y'uѺ^Ü—Òd~˜sÇsÝ ï<$õÏ欳ÎÏ ±3|èCº@ý8m|ÇüX–}ÂXúÖ)±¥<úÒøƒIŠ ´&çÄÍþYÄ#1Ú ~õ«_ †“Õý;ÌâŽÖ-ù'øþ÷¿ÿHcŸúÔ§ÃÑÕù‡Vòà)OyJ׆ / G[í¾¼S_ò|ààw¿ûÝHz5ô¥°öÚkw´í–·¼eמ&õ`xækŽM sg¿âéOúàcûØhkrŒ ‡~âK_úÒàÁ~ðhkuœÈï:ë¬Óɼò•¯ĨXŸõ¬g-ˆ5ãhÌ Vb­x±ýãÿ¸«ƒ;ÞñŽƒü࣭yŒõxÀà·¿ýíHb0^…Œo‚ÔkýïxÇàŸøD'+ØÌ[Æ¥Oʾ¹X:]mC ?Öû;‘û0}8¾=öØcðñ¼ë&ý`xVîúµ™ÔßÖ[o=øò—¿<&x·-ÐKÛY¯Û`õß!0¾ÕV[¶ƒáÿ{ã¤fpç˜r˜Õ‡Þñö·¿ýà!yÈHk$ëð4=Aäßö¶·$VcxTÛL"‹ŽuéD%vß}÷Nvxä騮>)+†GÐn(§nÖᓞô¤‘Æ<88ì¿ÿþƒáÑ®—è¶Xà &ì+ •y®ž »…”ñÖX±•=ñ–·¼e°é¦›NÄ—~­3®Äïÿû±LÒ>ò‘L$%‹—õP÷e݇èR¾ýíoï’?ñ•¯|¥»ë™¾R§57üžúÔ§v2gŽó>÷¹Ïàw¸ÃHc58UyJ}å×þð‡ã±d86Jô<øQ×NÊJlÀÖAŒ“…Æ-n1ü?aœŒ”iTg”£NNð‡?üáÁmo{Ûq?$HnÏç0úÓž¤ÍÁI{Ý™, O~ò“]?òN®ÄN;É~wHîHëìЊÛÝîvãþ>bã¾÷½ïHc_ûÚ׺¾Jã l->|¯:úÉúýîw¿‘ÆÓ•È8VJiN™Œ”ÖåÚüáL™Än¼ñÆcáVRö9䨞Û0e¾ñoŒ<¬†g-d`NÚ—vZ——gœqÆ„œpXÁØhW6w`î >]ãëÓ´U0ßúLn;Î Ú”OûÕwK7ç$å«.rú¥?ý{ì±#‰yä™+eõO™Ä6géz‰ ¸²JÆÙ77Êô‘~ÖrEúHÒÆåÿw>ãÏè®þÔ…Žr¥TÇ)MÊ$ë L$& ßýîw»Á?ï&%LÑ«u­#“ÿ‹ú°ã€$£™ö[—\€ðÕ¯“\e[:¶OÓ•©Ã"°¸+3T®Ú`¾*»þª<´mšnúlé"WѧËe`í96™í0× óÑú_œ³¦vô Ù®¨20Râ«¢«¼Õ­n5xéK_:’œël‹-¶èþÝãÌ®^Ž/ë9^Çl‚LHÑ%&ÙŸ@@#é$¥3¥3ØO?ýô®-\Úvrj½ =n2Tp72m9Áì  b²_¦Ž¾aþe z”èÚWÑÒM_¾ å‘M+ÔncMiÇ:%s[¡nê#öÙg$æA»ó'µm][®ÊÖ¾$ñÓŸ¶Ñ­ -eô•uì¸.Æ£ŸôwÞyçÁŸøÄ‘ô$8xpV}Ï{ÞÓ]u¦¾v¡ë–ñ¶H\5¶îR6ï>Ú0”Æq­ÛÞgp xÐÕNN†ÔÓäÿ™ Úµg¼ÚªÀwŽGYÛ$~+ðcìÓæiš®cR?ý·ôr®RVÚ‡\EúSN;êÒ×§[}ÃZ ávúîGз/]m±]A[& òÖé‡úKOúJp¯½ö¼ä%/i,ÄGÑ}LrØa‡-ð›ö¡ó ƒ$ ß•MpkXC5Øt˜Æëç4õ³™V’wÜqMûúÈ4èKxdˆÉ¸$Ê äÒ·þdËoÂq;É©c ”-¨‹ŒzÕw…>RÆR"ÓŠ·ÆÚ§ [H=u[øú׿>^ÌIcÖ§±€¾} ô‰žû±å÷cÝŸôkn€ý鯒3çÁÜ<»ƒË.»¬»kËGFüÛ–q`³Æ‘L¸=¾+›Àp °VGê2Á&"<ÃÜà]ir¶“á`ê rÓ›OÈeìÚ©@Nå)aÀŲKÕµ_ýZVÔØjœÖ[p߀ô›zÔû€®òÆÑs˜gJ™ûÒ¸qµÖ `|踯`…ýÚׇãÒß4¤/ÊjrS‡»ÿ»îºëHk!x°`»í¶üñãxÕ7&êÎ'ȺhgË_üâ;vbs‚q™`Ò¤Lòp œ5õ“¤M?úî̘8cr´WŒ>ªŸôׂ1Ô8ô½RWÆk,ÚVN jò‘ì¶'žxb·FZd —>[1 ?v|ŒYöçüQÖñµü¤eѧ^íÒFù‚¼ û¸ˆ²’“Ïöù8Éõ‡ž¥¶õÓB÷?&w—*øÌQc-ãPÚ”guVWÞð†7ìH½u[9ƒLû”5øôA™àN­mÈCõ±UA?~RÖ:ÐWõ#»|4´ýöÛwŸY>æ1éÚ$'}Ž?ç@»Ú´žìΘ뭷^g,Á-a>KÒ`§L#€1É ’Ï+wØa‡ñÙR¶Îš&fƒp2„“\?åΙ}ÀØxE«ß¶ã©ÀGîÐÊ”©°]]a,–­Ø€>Ónm&@k<‰”3¾|¦Z\~ùåã³D&fîË›6¿ùÍoŽZVƒO8ˆÓ'r~*œ™Ð‡¬ÈöìÇÎÞ{ïݽü>Í> Ê£…o}ë[G-óàÆŸl­oÇ•ö»3&øàq‚xùB#ÅF…ƒâ2ר<®U3¢Üv‡Äa² ðÓú Äta‹6S?QmtÕï[¬´é«úT¿OØWû·…V¬B[}þìÓ/¥H=˲Êgÿ=îqîQ¿Š/|á  OÖ}*°Y÷'Ô˜ÔiÍ6¥ÈxsÌ O&ùÔçž{nÓn%gMnü$8P™É:7ØÎùãhwÜqÇQmLØ~ûí×QÊ N8ŸU’”<ÔÎCâ˜ÁCÚô ¬Ï㤓Nš®e&¡ž-_þò—wÏÏ:¹À ê.åúÙ–¨;3™í)×'³œxÕ·Žï Û³¬uYÑ×GŒ<SÁ£™‹­“k,?ãæLù²—½¬‹MöÅ–hÍ™zÓlØÞ×Ê)§Œj«¡Lkî6ß|ó®/Á\˜õßÄt¾åDb¶ÎšÜ]Úf›mºKN¤Q'[É|÷ IÌ«<› yÖ°‚kqn7wžÌj?¾ ÉÛ9A­I[*”×e…2)W©\ UN8Æå mU{‚vâ̱Ը[ã)ã¶à èž÷¼çhk5®¸âŠîÁw÷[kîKðÁ~pkq¹HÝ>ýÅúßó“rÆå dz.Ÿ $6Ø`ƒn¬æL‹5d·7œ¤=÷ܳ++H÷Å/~q—t'Ÿ|rwî„NèÞJáAä}öÙ§“%ñH–Ö"c0¼Jö¡}hÔ2w¿ûÝ£Ú|r vl;äCÆOãgÅèáûœ8™í}¨²Y‚>ݺh¤¨Û¢Ê¶d–ŠÔM›é˸ݶ”-Lë磃zI{衇v—q¹[äl™‰Éç€\Q¥ŸôÛ°/Ùj¯hÍG‰Ih™Éyå•WŽ4VƒÿÃ's`ö%f&':‡I®‘8à€ÑÖB0Ù|ŒÂ“$ô}ôуK/½´ë'Iyó£: ^õyÿûß?ÚZŸýìgÝ?ÛB=¨nx )™0ÊœX':'¯N˜p¢­'ì«/¼‚Öãh m¨_Á\µÚãlùœíQ²*ˆ7ã©õiðJ¦Oޤd=ÔäÜm·Ýº³'È})IʽèE]?àÝNÖ}Â8s_j3A{ާÒvîW,6>Àþ ¾´‰OÏ”&&ÿ_ >NÊÿ9—9a‚Ö„tƒ.13(ÞòÎ7ó—NáÔÿk€W¢ú¶$® ®l¦-@ÛZñ¢; è’”Tù GÁÜr¿}Ì«€€ýDrpÅÄe0)ö!ÛܱÏýˆ]˜û~õ«_ĨÏíW&¸ÊkðZóÕr¬Mþ¥Ëx¸©Éã~NàÑ~tw—V9ÁøLÄÌI¿ ºÖðQ¾ügœP>çxá…vyœyæ™#³mð(´ò‘Ënt£Ž~nIP- â‹‚y2„g 9ó.v¦à³­7¾ñÝÍ%ìbßhËç2ùߘÅÈ-ëóÎ;oda¼Gz¯{Ý«»äÈ»Œ«V­êÓ©§žÚÉûðØÇ>vð¨G=ªûLïîw¿{×vþùç.¸à‚.ñût9¡ËÿgèÞâ·èô >[g=ÀKÛÄ‹ž—I€>Iè¾9Ä'wÑ…~Õ _'ÃX§éòa?ºèÜínwëâÅ7ÌD€Œ¯@1û@<¼¹Áׄ¸V\˜¬ ŸbòJ´Ïþó½sþdN›ìKæ‡ýÑæ•dBß±1–}÷Ý·³A¬ÄgóT’oÄ ËZÈ=ïyÏëÞ<1Ÿ\ŸÀ| ?È“ßøÆÌüYk¨Ì—ÿŒ ±¨™ ÞÈàeZ¾÷‡Ï3ù^ ˜ `g1©[wÝu»ÒÄ$èNC´ü±Ø‘rA3˜Ç=îq݃Î|r&¾63ÑMP^j¥OŸÈfiŸäJä|½ ßÒ1äpÈ?ù|_íœíYœ‹éZš(õ²ÝDêr€¢, åÓDFoŽu¹º|K!ÛS7ÁAÑMÚ$ÉYÔ¼$ŸûÒ¸îÜ—Gydw¦Å_Œò…Ë”¬Ø—Ž+ÇXur^ØèsF羈±òñ 0Ö©ó|sÄãÿønæú¦®c0gÈ“’ºÄÏ815f¢ÀúV>mô# p„frædC' w²þÜÐ 0Ø2hm;ÁØÊøµA»“Œ䫎z0'¿9&¶!2鋘)õ…>±¡'ÐÒ¥M]ý C™ófœP}kê:NüHôiêBÇKvkƪ>v>$:êAe@ƉŸ¤±‚ô™ã…ôUèGflÕ'¥ãé+Ǧä܇èSbƒö«¾±•Ä%}ÚdœØ€&d+1×ÖAN°å (¥2 "f‚IÚ[êS•úCN +µ•-»NnN¬t<Ös>¬k7K`‹‹¡¥kŸÛ"ûdEmoɵdú ¬t¾œ3ëÎweÊ:¯Àµã:Ê}™þ´íþ«ûSÆ’~ u@=áv–­úbÐŸã”Æf|RY}$Eqvè$&5¬&‘I5MÐÜvÔIÇ–v™dJN¸ýúÓW%íÂA§Ý´)G‹Æ—Ld[êô鶘ºIû@Ê·•«º¶'Üô%•o±ê:—­}™g©œsì×DîOl˶õÜVN,AŽÁí„qQÊÔIùV»:Æ‘1fœR9c¬6×Ù}÷Ý÷À°Té·®,ШÆ[ÛÒmõ@ÊÊÔq–°ê"ëDT]d*RO]ëи@ûƺúVOT]¾µ#rÈ[öëK¿´)ƒNúÓ§ºuœèO&èC.ÇÙ²£˜mÙgœ }gœêç¡ò‚ºzÊglU¯Ž¯êªG]]¨¨ž¥ý)ŸvHTévúZkx&ë"ò’‚Òëm.EýßÒ2/Q‘‡Wg) ÒA0!ø„žm݆"g©_“ki_K.Ïè’±dë8ôŒ-¯ \ôÊå˜ —>Õu‘C^fÜÈè[@Ùœkì «~ŒW} ¿Œ·êæXõOŸó$µ¥®úÀ’>å(S‡:P·Ö‘›Æç\ŸþŒÍ±éW¨'µ‰ŸÔ­‰“6€qJíX§Ú5¯,êÕâZC¥¡Î¼¡$;ÃA&i&fN€“–Ne.¦|úÖoÚNûL†>Ô¯T¡øsç¥?HÔGŽ;P™Ô3VÚ•S×RÿÈ¥ïÔêkC= .4€¬qê“6t‘A/ýQªëX§éªcI›ºÆ©®mÀ_úÔGêPB¡<°½ög|ÒùÈg|P_èÂÔ3VckÍOÅRb XuµGnä%?ÄÇ815s &¢ôάýê(ŽuÚÇÎq ßøL:QÆÔÑOnÛ/RÆm¾ƒ¾ýÈc%íÆ“1ª+útAŸ®~!qCôÎ5:È ãmÍ/rê¤_õÕU¿ú­ºÆÔUGÒ.ôO) 8VëÀ¾´£ŽL»Ä%ÕµŸñéGU/cDÞ9­óC=ËdB›Úƒyâ‚ôu‰‰‚F ŽÒÁ’$#´n²P`ÔàÓY²s< Ð r2œœ$íi ç¤P&ý•ޱŽê "‹ H¼ÖiÈdŒUªç|T]cH]¾µ•Q>çDy|É–žt¾2êçX²ë4Ýkú–© ”ÓŸÛrZ›6É}©ú‘Íݶ¿ŽO»@=çT}êÀx€:"íXê[Ø“në¯ûQ!!È@0413)¡ò¢&°Þ9.µ•vëd'„2 ¸t¤w¾¬WÄéX´ÐGã´MŸ²êêêS] ~– 嬋ôe]Ÿ)Ÿã„@¹«í¹Ö8~Й¦/Y} eÒ6}ê€míQ_îGÛ¡öŒ/íÇ”L=Ǥ.uû*ÐkA{€2í™ÉÎþP tJ”’¨4¥“à`Fs-ǰs>búM›ÔÅ~}XJ€Ïls;Q}0ëúI]Ç¢Mm¤ŽzKÕ­ú¶eÑs[¤®ò ý8·êå[ó©lÚH]åª.HyˆoÛ H_•Ê(Ÿz”ÚÕ.ú9.÷©eúP·ÚÚ‘©§N¥úՎȺà¹aÀ›Q¼xqÓ›Þtðæ7¿yðêW¿ºûÞ+.gùvÊ7¼á Ý#„ãŸáÓ%4È0t!»†Òe2º-•OßÚKÒž2 NNú­L¤Œ?ý¨gŒ/ÐF–P¨›v(•™¦ Ô“@¹–<0Fä-òÓ|¶b…Êɪ›òì[À6©Ÿ´O_ Ë´£ŒÚ!¦íWG[”Õ†TO¤nÆcI_Ê í ýQòr9_ó‰ =åÑJÞªyÝë^×½¨Ác©|­ÏÄïcZÕ0ÌÁºû´p9¨LJÛ ¤?¶-û€iL¤=Æ‘¥ý õ‰µ"í¸ Ó§ºn‹Ô•"u³L™–Žlù-”êuS?uR(G™ºÙž:É>T[ e[;Ž‹RjŸRùÔ· ¤ ·ÕS®úϾï"ó{¢¼cª= ,%oU}ë[ßêê<ÿË·qˆ¼Â0¼:6qÆnRÊ<¥ Ùm¨Ná€(³N™r ÚIÛÉŠj«ÖÝØÀ6 ¬~„zÄ+lS.õªn²…>] NÕU®%ŸÔS–ºcÏö>ýò,ºü©FÑš# ßKd;™²é»®‘ܶ­Új­¡Nê'¦éµhŸàïü­Ð«®ºª+öµäûŒx…1ñæ‰ÉkŒ\Âò²ý¼ñ³ 1MYæÀ[“…HÖÉ…Êõ«½Ü¶­¢Ú³²žúÓv¤¨¶*R/õÕ™¦ß§ R'ë}ò@_}þ@ŸÏ¥êò7,>X´v) AùÊG¾ûIÿé3ãp½PŠìÏq´X¡^ ©“º©“¥uÞn}ÇÕW_=aÇ:z|iojQ71ùáe^OäB^°îML`s–ÉÚVáDnÛÖ‡¥Øo!}e™H{Yf=õZ¶ZúUϲ¥3M¿¢% úüê³ÚXŽ.à FRòºZ‚¯Ÿá^^“ü&ßxÁ%^&1¿½Êå^~Ç>“ “³öQ“¹M©<Èz¢ê‹ô•¥àê1ðnmþ0×5×\ÓÍ0uMLÆÄÿ“$&¿²îhÑÄÙUëÒíŠLk`YOT[Ó|$–b;½j;}Tu;e³.úôÓ«H;‹É§ì4½Åâ]LWòžl.H¾­ÀËÖ¼"¯xÅ+Æïœ’—EÉÂÆ_pO‚z&¥‹ئý¾ —p;e@Ýʦª¿bÅŠ‰¯I!1éK t“9NÇ×mwµ¨ÜªcÄ:”•Ù?ÍN²¶å6v¦±O¯RP¯6jüKeê-G¿¥W¹ù”«zu»2û£:õ‹ÂY€$¤§ù¹7¥ð=Ã|ó„àûй‚<÷,’¶e™ Üýȶûµ/ÆÖØZò0uút¡>!°L89é'Ê(ïø(§&¦¨AômW.¥?Ñ’©¬hÉÀi}ÉåÈ%Z2Ó(Z}kš-´äú˜èë·LäbdñQæC)|«D‚ääUe”ËEŒ=™‹7QãZ —# «|nW89É—²ŽÍñ-)1Ev]¹´ô§½*[hÉ-•‹ùû¿Îœ¿XT& 3%% G¯ß¨à«?rÑö%©‹Ü… jÆv}RXÏ6aÒ»ÌñX§T^§Ä¬È€ûø¿­˜û¸\ùÿ‹tZp1²Ø Éå›I–|qŸçOØJP훜.bJ¹êx*§!ýè+ãÄ(‡“ó%Ðö•˜3̨ Qæë‚.À ™I Ýî[È,^ê0Ã…Ýâbýp1A½úÆhÌ9†¬›ÚN{³ÄœaÙp!¹ˆ ËÅ—ÉIÝäC¦B9X¢Úi§u?…ÁO®ó‹/|á »»¡_|qg³2c4ÎÚÆÇ9|ñøV[m5&ÛøjÉ÷ù¡L˜x-š˜$eR€r–˜3\'¸ˆ´¹Ðr!š\¯}Llºé¦c™ÉÉG,|îÉDó7>øüï˜%Aùž[¾®²&h.z·)Ñ'!ù¼ð.w¹ËàøÀ(’Õ?…€-úøŽY>O¬v$v õmÈ;9¹1U;³ÄœaÁY¥ ’Ôë·ÐóeË&m&'Üe—]:²`ùJVà•ùü“çL7Úh£ÎOßðÇ]tÑØ¿‹¾’o›¿ë]ïÚùN]¾Øœï­…øéIÀç³| ôŠáYYÝ´>Z‰YåMĬ+“6(á,1gX£ÈÅfbºyô,á7—ÛMR~þ‘סuîèšà/bæ·l|zˆ'‹øÂeΜØÓ¦uÈÃá>°ÓN;uO*g’ŸzÈŸ¤äÒ™äL[Æ`½„r´WôE;IHÝ„„`–˜3,.ž ˜‹Î’ß(áQ½ü ~‰™Ï0•ÉÅË™•ŸÌ$$gUû*<ðÀNœüìB•1¡I2A<úÔ¿äWïòqA~¯“³qÚJ2†Ûuê¼@Ûuç•r–˜3,}+$\ òZoPðL(?PÅšüÙ 72ø½©dáæo¨>ÿùÏŸHƒòfÅ þŶý\Sr¹ëóº<6Èï¤ÝÊm·Ý¶“2Û#˜%æ k ¼™?üðîÁl~¦Ñ_ÀâÇø±n¶ðã´œ1s1š˜üï˜I|ï{ß{"ajb@¾ Á £*ïq ~7…¶i6_ûÚ׎¤WãG?úQwÖ¬z°•˜Òq™p¹m[ ³ÄœaŸÒããÞáÇ„øu,Ξ¼±O; D‚º8 TòúS³*‹¼&„ä™Ûï5’@ž-¹ÑÃã‚ÿmýø&o6)¯.w‚¼Zå!1$2!¡c˺–‰YbÎp½¡.B)².êOæ÷,D"xæ"1¨s3(ÁY“>’ËÛ„zÉLHÉån‚Ç« l%fMNØo« Ìs†ë .:ʬg)|$®õ[¦Èš˜$49)ù™ÆDÊrM˜\ž5“´k×ßÀÄ‘>­["sÖ§a–˜3\¯ÈèÂÌ6’1_J¨ ?õ\ô&L›@­ö‘Põ§?LJ‰ 6ØÁ¦I ‘¥-Á¶‰Y“³ûRf–˜3ü .Lϲ/1¹´¥¸Ð]ø”²Úçæ’}è$N?ýô‰dl%e+1ùß{úVŽzBŸÈ—É (k¬-Ìs†5Ž\|­…h¢™¾”Ìwó‹Ó ~Ú݄ŎÉÉ¢—¹ðŸšD|<“à»]MÌd&¥É•À&mÚ…-¹l7¶:5ÞŠYbÎð? “Ò3¤‰IR 7ܰëÜÙU° Yì&†õ\è<keMÌ“N:i|¦4!óliâՄîÉF2¡Œ$ŽäR0KÌ®wäÒ³d+)ù6ò|:˜W\qÅØ ;=IA3»S†ÏBkrsÌ1ã³c&dž yHB`Ó¤2ÁÁ¾í"Û%¨å4Ìs†5Ž<ÃY' -kRúósüÝúë¯ß=V—8á„ÆúÀÅîâ¿ä’KF=ƒîŒëY7å¶Ùf›®Mðy©‰I2Zš”0¸çé%íi“Ò$L°-Sg9˜%æ × LÈLR’‹mJÏ’2¸uë­·žø\’§…Hmåb‡>W öÚk¯Qm5”áÁ¾·Uð± ßzNb™–$ÔÙgŸÝð˜aýLS`»BŸI‘õi˜%æ ×¼™8öØcGµÕȳ¥ôFO=[zÆô¬ÉëY™œü®7‚*8›š˜$e_^´æ]KqÐA N9å”.Y<»QòнoÂ<á O_çA&‘çƒújÛuÁ:{ð@ã 3,¼¨Ì¥a}JgÕªUƒ›Ýìfã;¢^ε ™ÉMPJen~ó›wÚñÑ—ª|WëQG5¸öÚk»:ïQò>% ÌÇûï¿ÿ`“M6™HÏVIçŸ~'ë`ø¸ÉMnÒ½2Æ#„¼]‚ o p¶ôL/´x¶—çp‘$*w—ùE/~ÎôjÁº®ÎßÐÁò.~gøÿ<ïÊ[ýÀÅiòA/-·ÜrËÁlÐm»(Y€ë®»nwf¤49]¬B;üïÇ£pKÌ–`Á%kâPæòÊ™ 9± cë ¦KÛÓG[}éCbS° R‡zÆÚg3ën«G™cw.ìŸ%æ ËB.2Jƒ2ÆRY®‹¯ÅnAŽäDËGm£„ m¥Íº-Ôo±"mh'ë u«Ô­c·ÞÉ •zŸa†䂳l%ŽÛ‰\­¶PíéÇ:}ÀhO›YZÚÏzn‹j/ëUß2õ2'ç`0ø‡!ÎãIEND®B`‚commons-pool-rel-commons-pool-2.10.0/src/site/site.xml000066400000000000000000000037541405425132200226440ustar00rootroot00000000000000 Commons Pool /images/pool-logo-white.png /index.html commons-pool-rel-commons-pool-2.10.0/src/site/xdoc/000077500000000000000000000000001405425132200221025ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/site/xdoc/download_pool.xml000066400000000000000000000332021405425132200254640ustar00rootroot00000000000000 Download Apache Commons Pool Apache Commons Documentation Team

We recommend you use a mirror to download our release builds, but you must verify the integrity of the downloaded files using signatures downloaded from our main distribution directories. Recent releases (48 hours) may not yet be available from all the mirrors.

You are currently using [preferred]. If you encounter a problem with this mirror, please select another mirror. If all mirrors are failing, there are backup mirrors (at the end of the mirrors list) that should be available.

[if-any logo][end]

Other mirrors:

It is essential that you verify the integrity of downloaded files, preferably using the PGP signature (*.asc files); failing that using the SHA512 hash (*.sha512 checksum files).

The KEYS file contains the public PGP keys used by Apache Commons developers to sign releases.

commons-pool2-2.10.0-bin.tar.gz sha512 pgp
commons-pool2-2.10.0-bin.zip sha512 pgp
commons-pool2-2.10.0-src.tar.gz sha512 pgp
commons-pool2-2.10.0-src.zip sha512 pgp
commons-pool2-2.6.2-bin.tar.gz sha512 pgp
commons-pool2-2.6.2-bin.zip sha512 pgp
commons-pool2-2.6.2-src.tar.gz sha512 pgp
commons-pool2-2.6.2-src.zip sha512 pgp
commons-pool2-2.4.3-bin.tar.gz sha256 pgp
commons-pool2-2.4.3-bin.zip sha256 pgp
commons-pool2-2.4.3-src.tar.gz sha256 pgp
commons-pool2-2.4.3-src.zip sha256 pgp
commons-pool-1.6-bin.tar.gz sha256 pgp
commons-pool-1.6-bin.zip sha256 pgp
commons-pool-1.6-src.tar.gz sha256 pgp
commons-pool-1.6-src.zip sha256 pgp

Older releases can be obtained from the archives.

commons-pool-rel-commons-pool-2.10.0/src/site/xdoc/downloads.xml000066400000000000000000000052241405425132200246210ustar00rootroot00000000000000 Downloads Apache Commons Documentation Team $Id$

The latest release binary and source releases are always available on the Apache Commons Pool Downloads page.

  • Version 2 is a major refactoring. The primary change is in the underlying implementation which uses significantly less locking and is therefore much better suited to high concurrency environments. The API has also been made much more self-consistent with a number of attributes changing name to clarify their purpose.
  • Version 1.6 adds generics based on the latest 1.5.x release, 1.5.7 at the present time. It requires Java 1.5.
  • Versions 1.3 through 1.5 depend at runtime only on a Java 1.3 or better JVM.
  • Version 1.2 depends at runtime on commons-collections.

Older releases are retained by the Apache Software Foundation but are moved into a special archive area.

Access to the source tree to see the latest and greatest code is possible through anonymous SVN access.

commons-pool-rel-commons-pool-2.10.0/src/site/xdoc/examples.xml000066400000000000000000000146631405425132200244540ustar00rootroot00000000000000 Examples Apache Commons Documentation Team $Id$

Suppose you're writing a set of java.io.Reader utilities, and would like to provide a method for dumping the contents of a Reader to a String. Here's the code for the ReaderUtil, implemented without an ObjectPool:

import java.io.Reader; import java.io.IOException; public class ReaderUtil { public ReaderUtil() { } /** * Dumps the contents of the {@link Reader} to a * String, closing the {@link Reader} when done. */ public String readToString(Reader in) throws IOException { StringBuffer buf = new StringBuffer(); try { for(int c = in.read(); c != -1; c = in.read()) { buf.append((char)c); } return buf.toString(); } catch(IOException e) { throw e; } finally { try { in.close(); } catch(Exception e) { // ignored } } } }

For the sake of this example, let's assume we want to pool the StringBuffers used to buffer the Reader's contents. (A pool of StringBuffers may or may not be useful in practice. We're just using it as a simple example here.)

Let's further assume that a complete pool implementation will be provided via a constructor. (We'll show you how to create such an implementation in just a moment.) Then to use the pool we simply call borrowObject to obtain the buffer, and then call returnObject when we're done with it. Then a ReaderUtil implementation using a pool of StringBuffers might look like this:

import java.io.IOException; import java.io.Reader; import org.apache.commons.pool2.ObjectPool; public class ReaderUtil { private ObjectPool<StringBuffer> pool; public ReaderUtil(ObjectPool<StringBuffer> pool) { this.pool = pool; } /** * Dumps the contents of the {@link Reader} to a String, closing the {@link Reader} when done. */ public String readToString(Reader in) throws IOException { StringBuffer buf = null; try { buf = pool.borrowObject(); for (int c = in.read(); c != -1; c = in.read()) { buf.append((char) c); } return buf.toString(); } catch (IOException e) { throw e; } catch (Exception e) { throw new RuntimeException("Unable to borrow buffer from pool" + e.toString()); } finally { try { in.close(); } catch (Exception e) { // ignored } try { if (null != buf) { pool.returnObject(buf); } } catch (Exception e) { // ignored } } } }

Since we've constrained ourselves to the ObjectPool interface, an arbitrary pool implementation (returning, in our case, StringBuffers) can be used. When a different or "better" pool implementation comes along, we can simply drop it into our ReaderUtil without changing a line of code.

The implementations provided in pool2 wrap pooled objects in PooledObject wrappers for internal use by the pool and object factories. The PooledObjectFactory interface defines lifecycle methods for pooled objects. The simplest way to implement a PoolableObjectFactory is to extend BasePooledObjectFactory.

Here's a PooledObjectFactory implementation that creates StringBuffers as used above.

import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.impl.DefaultPooledObject; public class StringBufferFactory extends BasePooledObjectFactory<StringBuffer> { @Override public StringBuffer create() { return new StringBuffer(); } /** * Use the default PooledObject implementation. */ @Override public PooledObject<StringBuffer> wrap(StringBuffer buffer) { return new DefaultPooledObject<StringBuffer>(buffer); } /** * When an object is returned to the pool, clear the buffer. */ @Override public void passivateObject(PooledObject<StringBuffer> pooledObject) { pooledObject.getObject().setLength(0); } // for all other methods, the no-op implementation // in BasePooledObjectFactory will suffice }

We can, for example, use this factory with the GenericObjectPool to instantiate our ReaderUtil as follows:

ReaderUtil readerUtil = new ReaderUtil(new GenericObjectPool<StringBuffer>(new StringBufferFactory()));
commons-pool-rel-commons-pool-2.10.0/src/site/xdoc/guide/000077500000000000000000000000001405425132200231775ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/site/xdoc/guide/index.xml000066400000000000000000000055341405425132200250370ustar00rootroot00000000000000 Developers Guide Apache Commons Documentation Team

ObjectPool defines a simple pooling interface.

  • GenericObjectPool: ObjectPool implementation with configurable LIFO/FIFO behavior. The default behavior is for the pool to act as a LIFO queue. What this means is that when there are idle objects available in the pool, borrowObject returns the most recently returned ("last in") instance. If the lifo the property of the pool false, instances are returned in the opposite order - first-in, first-out.
  • SoftReferenceObjectPool: ObjectPool implementation with a LIFO (Last In First Out) behavior. Additionally this pool wraps each object in a SoftReference allowing the garbage collector to remove them in response to memory demand.

A KeyedObjectPool pools instances of multiple types. Each type may be accessed using an arbitrary key.

  • GenericKeyedObjectPool: ObjectPool implementation with configurable LIFO/FIFO behavior. The default behavior is for the pool to act as a LIFO queue. What this means is that when there are idle objects available in the pool, borrowObject returns the most recently returned ("last in") instance. If the lifo the property of the pool false, instances are returned in the opposite order - first-in, first-out.
commons-pool-rel-commons-pool-2.10.0/src/site/xdoc/index.xml000066400000000000000000000217601405425132200237410ustar00rootroot00000000000000 Overview Apache Commons Documentation Team $Id$

The Apache Commons Pool open source software library provides an object-pooling API and a number of object pool implementations. Version 2 of Apache Commons Pool contains a completely re-written pooling implementation compared to the 1.x series. In addition to performance and scalability improvements, version 2 includes robust instance tracking and pool monitoring.

  • Version 2.10.x requires Java 8 or above.
  • Version 2.9.x requires Java 8 or above.
  • Version 2.8.x requires Java 8 or above.
  • Version 2.7.x requires Java 8 or above.
  • Version 2.6.x requires Java 7 or above.
  • Version 2.5.x requires Java 7 or above.
  • Version 2.0 requires 6 or above.

See the downloads page for information on obtaining releases.

The org.apache.commons.pool2 package defines a handful of pooling interfaces and some base classes that may be useful when creating new pool implementations.

PooledObjectFactory provides a generic interface for managing the lifecycle of a pooled object:

public interface PooledObjectFactory<T> { PooledObject<T> makeObject(); void activateObject(PooledObject<T> obj); void passivateObject(PooledObject<T> obj); boolean validateObject(PooledObject<T> obj); void destroyObject(PooledObject<T> obj); }

Users of 1.x versions of Commons Pool will notice that while the PoolableObjectFactorys used by 1.x pools create and manage pooled objects directly, version 2 PooledObjectFactorys create and manage PooledObjects. These object wrappers maintain object pooling state, enabling PooledObjectFactory methods to have access to data such as instance creation time or time of last use. A DefaultPooledObject is provided, with natural implementations for pooling state methods. The simplest way to implement a PoolableObjectFactory is to have it extend BasePooledObjectFactory. This factory provides a makeObject() that returns wrap(create()) where create and wrap are abstract. You provide an implementation of create to create the underlying objects that you want to manage in the pool and wrap to wrap created instances in PooledObjects. To use DefaultPooledObject wrappers, use @Override public PooledObject<Foo> wrap(Foo foo) { return new DefaultPooledObject<Foo>(foo); } where Foo is the type of the objects being pooled (the return type of create()).

KeyedPooledObjectFactory defines a similar interface for KeyedObjectPools:

public interface KeyedPoolableObjectFactory<K,V> { PooledObject<V> makeObject(K key); void activateObject(K key, PooledObject<V> obj); void passivateObject(K key, PooledObject<V> obj); boolean validateObject(K key, PooledObject<V> obj); void destroyObject(K key, PooledObject<V> obj); }

BaseKeyedPooledObjectFactory provides an abstract base implementation of KeyedPooledObjectFactory.

The org.apache.commons.pool2.impl package provides some Pool implementations.

GenericObjectPool provides a wide variety of configuration options, including the ability to cap the number of idle or active instances, to evict instances as they sit idle in the pool, etc. As of version 2, GenericObjectPool also provides abandoned instance tracking and removal.

GenericKeyedObjectPool offers the same behavior for keyed pools.

SoftReferenceObjectPool can grow as needed, but allows the garbage collector to evict idle instances from the pool as needed.

Client code that uses a Pool 2.x release should require no code changes to work with a later Pool 2.x release.

New Pool 2.x releases may include support for new configuration attributes. These will be listed in the change log. Note that the MBean interfaces (those with names ending in MXBean or MBean) such as DefaultPooledObjectInfoMBean, GenericKeyedObjectPoolMXBean or GenericKeyedObjectPoolMXBean may change from one release to the next to support these new attributes. These interfaces should, therefore, not be implemented by client as the changes will not be backwards compatible.

The migration from Apache Commons Pool 1.x to 2.x will require some code changes. The most significant changes are the changes in package name from org.apache.commons.pool to org.apache.commons.pool2 and the change in the implementation classes to use PooledObjectFactorys, as described above.

The key implementation classes (GenericObjectPool and GenericKeyedObjectPool) have retained their names so no changes should be required there although a number of attributes have been renamed to improve consistency and ensure attributes with the same name in different pools have the same meaning. It is likely that some changes will be required to use the new attribute names.

commons-pool-rel-commons-pool-2.10.0/src/site/xdoc/issue-tracking.xml000066400000000000000000000133421405425132200255570ustar00rootroot00000000000000 Apache Commons Pool Issue tracking Apache Commons Documentation Team

Apache Commons Pool uses ASF JIRA for tracking issues. See the Apache Commons Pool JIRA project page.

To use JIRA you may need to create an account (if you have previously created/updated Commons issues using Bugzilla an account will have been automatically created and you can use the Forgot Password page to get a new password).

If you would like to report a bug, or raise an enhancement request with Apache Commons Pool please do the following:

  1. Search existing open bugs. If you find your issue listed then please add a comment with your details.
  2. Search the mailing list archive(s). You may find your issue or idea has already been discussed.
  3. Decide if your issue is a bug or an enhancement.
  4. Submit either a bug report or enhancement request.

Please also remember these points:

  • the more information you provide, the better we can help you
  • test cases are vital, particularly for any proposed enhancements
  • the developers of Apache Commons Pool are all unpaid volunteers

For more information on subversion and creating patches see the Apache Contributors Guide.

You may also find these links useful:

commons-pool-rel-commons-pool-2.10.0/src/site/xdoc/mail-lists.xml000066400000000000000000000237131405425132200247100ustar00rootroot00000000000000 Apache Commons Pool Mailing Lists Apache Commons Documentation Team

Apache Commons Pool shares mailing lists with all the other Commons Components. To make it easier for people to only read messages related to components they are interested in, the convention in Commons is to prefix the subject line of messages with the component's name, for example:

  • [pool] Problem with the ...

Questions related to the usage of Apache Commons Pool should be posted to the User List.
The Developer List is for questions and discussion related to the development of Apache Commons Pool.
Please do not cross-post; developers are also subscribed to the user list.
You must be subscribed to post to the mailing lists. Follow the Subscribe links below to subscribe.

Note: please don't send patches or attachments to any of the mailing lists. Patches are best handled via the Issue Tracking system. Otherwise, please upload the file to a public server and include the URL in the mail.

Please prefix the subject line of any messages for Apache Commons Pool with [pool] - thanks!

Name Subscribe Unsubscribe Post Archive Other Archives
Commons User List

Questions on using Apache Commons Pool.

Subscribe Unsubscribe Post mail-archives.apache.org
lists.apache.org
markmail.org
www.mail-archive.com
news.gmane.org
Commons Developer List

Discussion of development of Apache Commons Pool.

Subscribe Unsubscribe Post mail-archives.apache.org
lists.apache.org
markmail.org
www.mail-archive.com
news.gmane.org
Commons Issues List

Only for e-mails automatically generated by the issue tracking system.

Subscribe Unsubscribe read only mail-archives.apache.org
lists.apache.org
markmail.org
www.mail-archive.com
Commons Commits List

Only for e-mails automatically generated by the source control sytem.

Subscribe Unsubscribe read only mail-archives.apache.org
lists.apache.org
markmail.org
www.mail-archive.com

Other mailing lists which you may find useful include:

Name Subscribe Unsubscribe Post Archive Other Archives
Apache Announce List

General announcements of Apache project releases.

Subscribe Unsubscribe read only mail-archives.apache.org
lists.apache.org
markmail.org
old.nabble.com
www.mail-archive.com
news.gmane.org
commons-pool-rel-commons-pool-2.10.0/src/test/000077500000000000000000000000001405425132200211605ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/000077500000000000000000000000001405425132200221015ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/000077500000000000000000000000001405425132200226705ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/000077500000000000000000000000001405425132200241115ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/000077500000000000000000000000001405425132200255645ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/000077500000000000000000000000001405425132200266175ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/MethodCall.java000066400000000000000000000066251405425132200315070ustar00rootroot00000000000000/* * 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.commons.pool2; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; /** * Holds method names, parameters, and return values for tracing method calls. */ public class MethodCall { private final String name; private final List params; private Object returned; public MethodCall(final String name) { this(name, null); } public MethodCall(final String name, final List params) { if (name == null) { throw new IllegalArgumentException("name must not be null."); } this.name = name; if (params != null) { this.params = params; } else { this.params = Collections.emptyList(); } } public MethodCall(final String name, final Object param) { this(name, Collections.singletonList(param)); } public MethodCall(final String name, final Object param1, final Object param2) { this(name, Arrays.asList(param1, param2)); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final MethodCall that = (MethodCall)o; if (!Objects.equals(name, that.name)) { return false; } if (!Objects.equals(params, that.params)) { return false; } return Objects.equals(returned, that.returned); } public String getName() { return name; } public List getParams() { return params; } public Object getReturned() { return returned; } @Override public int hashCode() { int result; result = name != null ? name.hashCode() : 0; result = 29 * result + (params != null ? params.hashCode() : 0); result = 29 * result + (returned != null ? returned.hashCode() : 0); return result; } public MethodCall returned(final Object obj) { setReturned(obj); return this; } public void setReturned(final Object returned) { this.returned = returned; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("MethodCall"); sb.append("{name='").append(name).append('\''); if (!params.isEmpty()) { sb.append(", params=").append(params); } if (returned != null) { sb.append(", returned=").append(returned); } sb.append('}'); return sb.toString(); } } MethodCallPoolableObjectFactory.java000066400000000000000000000116061405425132200355600ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/* * 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.commons.pool2; import java.util.ArrayList; import java.util.List; import org.apache.commons.pool2.impl.DefaultPooledObject; /** * A poolable object factory that tracks how {@link MethodCall methods are called}. * * @see MethodCall */ public class MethodCallPoolableObjectFactory implements PooledObjectFactory { private final List methodCalls = new ArrayList<>(); private int count; private boolean valid = true; private boolean makeObjectFail; private boolean activateObjectFail; private boolean validateObjectFail; private boolean passivateObjectFail; private boolean destroyObjectFail; @Override public void activateObject(final PooledObject obj) throws Exception { methodCalls.add(new MethodCall("activateObject", obj.getObject())); if (activateObjectFail) { throw new PrivateException("activateObject"); } } @Override public void destroyObject(final PooledObject obj) throws Exception { methodCalls.add(new MethodCall("destroyObject", obj.getObject())); if (destroyObjectFail) { throw new PrivateException("destroyObject"); } } public int getCurrentCount() { return count; } public List getMethodCalls() { return methodCalls; } public boolean isActivateObjectFail() { return activateObjectFail; } public boolean isDestroyObjectFail() { return destroyObjectFail; } public boolean isMakeObjectFail() { return makeObjectFail; } public boolean isPassivateObjectFail() { return passivateObjectFail; } public boolean isValid() { return valid; } public boolean isValidateObjectFail() { return validateObjectFail; } @Override public PooledObject makeObject() throws Exception { final MethodCall call = new MethodCall("makeObject"); methodCalls.add(call); final int originalCount = this.count++; if (makeObjectFail) { throw new PrivateException("makeObject"); } // Generate new object, don't use cache via Integer.valueOf(...) final Integer obj = Integer.valueOf(originalCount); call.setReturned(obj); return new DefaultPooledObject<>(obj); } @Override public void passivateObject(final PooledObject obj) throws Exception { methodCalls.add(new MethodCall("passivateObject", obj.getObject())); if (passivateObjectFail) { throw new PrivateException("passivateObject"); } } public void reset() { count = 0; getMethodCalls().clear(); setMakeObjectFail(false); setActivateObjectFail(false); setValid(true); setValidateObjectFail(false); setPassivateObjectFail(false); setDestroyObjectFail(false); } public void setActivateObjectFail(final boolean activateObjectFail) { this.activateObjectFail = activateObjectFail; } public void setCurrentCount(final int count) { this.count = count; } public void setDestroyObjectFail(final boolean destroyObjectFail) { this.destroyObjectFail = destroyObjectFail; } public void setMakeObjectFail(final boolean makeObjectFail) { this.makeObjectFail = makeObjectFail; } public void setPassivateObjectFail(final boolean passivateObjectFail) { this.passivateObjectFail = passivateObjectFail; } public void setValid(final boolean valid) { this.valid = valid; } public void setValidateObjectFail(final boolean validateObjectFail) { this.validateObjectFail = validateObjectFail; } @Override public boolean validateObject(final PooledObject obj) { final MethodCall call = new MethodCall("validateObject", obj.getObject()); methodCalls.add(call); if (validateObjectFail) { throw new PrivateException("validateObject"); } final boolean r = valid; call.returned(Boolean.valueOf(r)); return r; } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/ObjectPoolIssue326.java000066400000000000000000000165171405425132200330000ustar00rootroot00000000000000/* * 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.commons.pool2; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.commons.pool2.impl.BaseObjectPoolConfig; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericKeyedObjectPool; import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; /** * On my box with 4 cores this test fails at between 5s and 900s with an average * of 240s (data from 10 runs of test). * * It is hard to turn this in a unit test because it can affect the build * negatively since you need to run it for a while. */ public final class ObjectPoolIssue326 { private class ObjectFactory extends BaseKeyedPooledObjectFactory { @Override public Object create(final Integer s) throws Exception { return new TestObject(); } @Override public PooledObject wrap(final Object o) { return new DefaultPooledObject<>(o); } } private class Task implements Callable { private final GenericKeyedObjectPool m_pool; private final int m_key; Task(final GenericKeyedObjectPool pool, final int count) { m_pool = pool; m_key = count % 20; } private void busyWait(final long timeMillis) { // busy waiting intentionally as a simple thread.sleep fails to reproduce final long endTimeMillis = System.currentTimeMillis() + timeMillis; while (System.currentTimeMillis() < endTimeMillis) { // empty } } @Override public Object call() throws Exception { try { final Object value; value = m_pool.borrowObject(m_key); // don't make this too long or it won't reproduce, and don't make it zero or it // won't reproduce // constant low value also doesn't reproduce busyWait(System.currentTimeMillis() % 4); m_pool.returnObject(m_key, value); return "success"; } catch (final NoSuchElementException e) { // ignore, we've exhausted the pool // not sure whether what we do here matters for reproducing busyWait(System.currentTimeMillis() % 20); return "exhausted"; } } } private class TestObject { } public static void main(final String[] args) { try { new ObjectPoolIssue326().run(); } catch (final Exception e) { e.printStackTrace(); } } private List createTasks(final GenericKeyedObjectPool pool) { final List tasks = new ArrayList<>(); for (int i = 0; i < 250; i++) { tasks.add(new Task(pool, i)); } return tasks; } private void run() throws Exception { final GenericKeyedObjectPoolConfig poolConfig = new GenericKeyedObjectPoolConfig(); poolConfig.setMaxTotal(10); poolConfig.setMaxTotalPerKey(5); poolConfig.setMinIdlePerKey(-1); poolConfig.setMaxIdlePerKey(-1); poolConfig.setLifo(true); poolConfig.setFairness(true); poolConfig.setMaxWaitMillis(30 * 1000); poolConfig.setMinEvictableIdleTime(Duration.ofMillis(-1)); poolConfig.setSoftMinEvictableIdleTime(Duration.ofMillis(-1)); poolConfig.setNumTestsPerEvictionRun(1); poolConfig.setTestOnCreate(false); poolConfig.setTestOnBorrow(false); poolConfig.setTestOnReturn(false); poolConfig.setTestWhileIdle(false); poolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(5)); poolConfig.setEvictionPolicyClassName(BaseObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME); poolConfig.setBlockWhenExhausted(false); poolConfig.setJmxEnabled(false); poolConfig.setJmxNameBase(null); poolConfig.setJmxNamePrefix(null); final GenericKeyedObjectPool pool = new GenericKeyedObjectPool<>(new ObjectFactory(), poolConfig); // number of threads to reproduce is finicky. this count seems to be best for my // 4 core box. // too many doesn't reproduce it ever, too few doesn't either. final ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); final long startTimeMillis = System.currentTimeMillis(); long testIter = 0; try { while (true) { testIter++; if (testIter % 1000 == 0) { System.out.println(testIter); } final List tasks = createTasks(pool); final List> futures = service.invokeAll(tasks); for (final Future future : futures) { future.get(); } } } finally { System.out.println("Time: " + (System.currentTimeMillis() - startTimeMillis) / 1000.0); service.shutdown(); } } } /* * * Example stack trace: java.util.concurrent.ExecutionException: * java.lang.NullPointerException at * java.util.concurrent.FutureTask.report(FutureTask.java:122) at * java.util.concurrent.FutureTask.get(FutureTask.java:192) at * threading_pool.ObjectPoolIssue.run(ObjectPoolIssue.java:63) at * threading_pool.ObjectPoolIssue.main(ObjectPoolIssue.java:23) at * sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at * sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) * at * sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl. * java:43) at java.lang.reflect.Method.invoke(Method.java:498) at * com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) Caused * by: java.lang.NullPointerException at * org.apache.commons.pool2.impl.GenericKeyedObjectPool.returnObject( * GenericKeyedObjectPool.java:474) at * threading_pool.ObjectPoolIssue$Task.call(ObjectPoolIssue.java:112) at * java.util.concurrent.FutureTask.run(FutureTask.java:266) at * java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java: * 1142) at * java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java: * 617) at java.lang.Thread.run(Thread.java:745) * */ commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/PoolTest.java000066400000000000000000000073411405425132200312400ustar00rootroot00000000000000/* * 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.commons.pool2; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import java.time.Duration; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @Disabled public class PoolTest { private static class Foo { } private static class PooledFooFactory implements PooledObjectFactory { private static final long VALIDATION_WAIT_IN_MILLIS = 1000; @Override public void activateObject(final PooledObject pooledObject) throws Exception { } @Override public void destroyObject(final PooledObject pooledObject) throws Exception { } @Override public PooledObject makeObject() throws Exception { return new DefaultPooledObject<>(new Foo()); } @Override public void passivateObject(final PooledObject pooledObject) throws Exception { } @Override public boolean validateObject(final PooledObject pooledObject) { try { Thread.sleep(VALIDATION_WAIT_IN_MILLIS); } catch (final InterruptedException e) { Thread.interrupted(); } return false; } } private static final CharSequence COMMONS_POOL_EVICTIONS_TIMER_THREAD_NAME = "commons-pool-EvictionTimer"; private static final long EVICTION_PERIOD_IN_MILLIS = 100; @Test public void testPool() throws Exception { final GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); poolConfig.setTestWhileIdle(true /* testWhileIdle */); final PooledFooFactory pooledFooFactory = new PooledFooFactory(); try (GenericObjectPool pool = new GenericObjectPool<>(pooledFooFactory, poolConfig)) { pool.setTimeBetweenEvictionRunsMillis(EVICTION_PERIOD_IN_MILLIS); assertEquals(EVICTION_PERIOD_IN_MILLIS, pool.getTimeBetweenEvictionRuns().toMillis()); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(EVICTION_PERIOD_IN_MILLIS)); assertEquals(EVICTION_PERIOD_IN_MILLIS, pool.getTimeBetweenEvictionRuns().toMillis()); pool.addObject(); try { Thread.sleep(EVICTION_PERIOD_IN_MILLIS); } catch (final InterruptedException e) { Thread.interrupted(); } } final Thread[] threads = new Thread[Thread.activeCount()]; Thread.enumerate(threads); for (final Thread thread : threads) { if (thread == null) { continue; } final String name = thread.getName(); assertFalse( name.contains(COMMONS_POOL_EVICTIONS_TIMER_THREAD_NAME),name); } } }commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/PrivateException.java000066400000000000000000000021061405425132200327520ustar00rootroot00000000000000/* * 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.commons.pool2; /** * An exception that only is thrown by these tests. */ public class PrivateException extends RuntimeException { private static final long serialVersionUID = 1L; public PrivateException(final String message) { super(message); } } TestBaseKeyedPoolableObjectFactory.java000066400000000000000000000034331405425132200362370ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/* * 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.commons.pool2; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.junit.jupiter.api.Test; /** */ public class TestBaseKeyedPoolableObjectFactory { private static class TestFactory extends BaseKeyedPooledObjectFactory { @Override public Object create(final Object key) throws Exception { return null; } @Override public PooledObject wrap(final Object value) { return new DefaultPooledObject<>(value); } } @Test public void testDefaultMethods() throws Exception { final KeyedPooledObjectFactory factory = new TestFactory(); factory.activateObject("key",null); // a no-op factory.passivateObject("key",null); // a no-op factory.destroyObject("key",null); // a no-op assertTrue(factory.validateObject("key",null)); // constant true } }commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/TestBaseObjectPool.java000066400000000000000000000237261405425132200331670ustar00rootroot00000000000000/* * 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.commons.pool2; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; /** */ public class TestBaseObjectPool extends TestObjectPool { private static class TestObjectPool extends BaseObjectPool { @Override public Object borrowObject() { return null; } @Override public void invalidateObject(final Object obj) { } @Override public void returnObject(final Object obj) { } } private ObjectPool _pool = null; /** * @param n Ignored by this implemented. Used by sub-classes. * * @return the Nth object (zero indexed) */ protected Object getNthObject(final int n) { if (this.getClass() != TestBaseObjectPool.class) { fail("Subclasses of TestBaseObjectPool must reimplement this method."); } throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation."); } protected boolean isFifo() { if (this.getClass() != TestBaseObjectPool.class) { fail("Subclasses of TestBaseObjectPool must reimplement this method."); } return false; } protected boolean isLifo() { if (this.getClass() != TestBaseObjectPool.class) { fail("Subclasses of TestBaseObjectPool must reimplement this method."); } return false; } /** * @param minCapacity Ignored by this implemented. Used by sub-classes. * * @return A newly created empty pool */ protected ObjectPool makeEmptyPool(final int minCapacity) { if (this.getClass() != TestBaseObjectPool.class) { fail("Subclasses of TestBaseObjectPool must reimplement this method."); } throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation."); } @Override protected ObjectPool makeEmptyPool(final PooledObjectFactory factory) { if (this.getClass() != TestBaseObjectPool.class) { fail("Subclasses of TestBaseObjectPool must reimplement this method."); } throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation."); } @Test public void testBaseAddObject() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException e) { return; // skip this test if unsupported } try { assertEquals(0, _pool.getNumIdle()); assertEquals(0, _pool.getNumActive()); _pool.addObject(); assertEquals(1, _pool.getNumIdle()); assertEquals(0, _pool.getNumActive()); final String obj = _pool.borrowObject(); assertEquals(getNthObject(0), obj); assertEquals(0, _pool.getNumIdle()); assertEquals(1, _pool.getNumActive()); _pool.returnObject(obj); assertEquals(1, _pool.getNumIdle()); assertEquals(0, _pool.getNumActive()); } catch(final UnsupportedOperationException e) { return; // skip this test if one of those calls is unsupported } finally { _pool.close(); } } @Test public void testBaseBorrow() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException e) { return; // skip this test if unsupported } assertEquals(getNthObject(0), _pool.borrowObject()); assertEquals(getNthObject(1), _pool.borrowObject()); assertEquals(getNthObject(2), _pool.borrowObject()); _pool.close(); } @Test public void testBaseBorrowReturn() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException e) { return; // skip this test if unsupported } String obj0 = _pool.borrowObject(); assertEquals(getNthObject(0), obj0); String obj1 = _pool.borrowObject(); assertEquals(getNthObject(1), obj1); String obj2 = _pool.borrowObject(); assertEquals(getNthObject(2), obj2); _pool.returnObject(obj2); obj2 = _pool.borrowObject(); assertEquals(getNthObject(2), obj2); _pool.returnObject(obj1); obj1 = _pool.borrowObject(); assertEquals(getNthObject(1), obj1); _pool.returnObject(obj0); _pool.returnObject(obj2); obj2 = _pool.borrowObject(); if (isLifo()) { assertEquals(getNthObject(2),obj2); } if (isFifo()) { assertEquals(getNthObject(0),obj2); } obj0 = _pool.borrowObject(); if (isLifo()) { assertEquals(getNthObject(0),obj0); } if (isFifo()) { assertEquals(getNthObject(2),obj0); } _pool.close(); } @Test public void testBaseClear() throws Exception { try { _pool = makeEmptyPool(3); } catch (final UnsupportedOperationException e) { return; // skip this test if unsupported } assertEquals(0, _pool.getNumActive()); assertEquals(0, _pool.getNumIdle()); final String obj0 = _pool.borrowObject(); final String obj1 = _pool.borrowObject(); assertEquals(2, _pool.getNumActive()); assertEquals(0, _pool.getNumIdle()); _pool.returnObject(obj1); _pool.returnObject(obj0); assertEquals(0, _pool.getNumActive()); assertEquals(2, _pool.getNumIdle()); _pool.clear(); assertEquals(0, _pool.getNumActive()); assertEquals(0, _pool.getNumIdle()); final Object obj2 = _pool.borrowObject(); assertEquals(getNthObject(2), obj2); _pool.close(); } @Test public void testBaseClosePool() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException e) { return; // skip this test if unsupported } final String obj = _pool.borrowObject(); _pool.returnObject(obj); _pool.close(); try { _pool.borrowObject(); fail("Expected IllegalStateException"); } catch(final IllegalStateException e) { // expected } } @Test public void testBaseInvalidateObject() throws Exception { try { _pool = makeEmptyPool(3); } catch (final UnsupportedOperationException e) { return; // skip this test if unsupported } assertEquals(0, _pool.getNumActive()); assertEquals(0, _pool.getNumIdle()); final String obj0 = _pool.borrowObject(); final String obj1 = _pool.borrowObject(); assertEquals(2, _pool.getNumActive()); assertEquals(0, _pool.getNumIdle()); _pool.invalidateObject(obj0); assertEquals(1, _pool.getNumActive()); assertEquals(0, _pool.getNumIdle()); _pool.invalidateObject(obj1); assertEquals(0, _pool.getNumActive()); assertEquals(0, _pool.getNumIdle()); _pool.close(); } @Test public void testBaseNumActiveNumIdle() throws Exception { try { _pool = makeEmptyPool(3); } catch (final UnsupportedOperationException e) { return; // skip this test if unsupported } assertEquals(0, _pool.getNumActive()); assertEquals(0, _pool.getNumIdle()); final String obj0 = _pool.borrowObject(); assertEquals(1, _pool.getNumActive()); assertEquals(0, _pool.getNumIdle()); final String obj1 = _pool.borrowObject(); assertEquals(2, _pool.getNumActive()); assertEquals(0, _pool.getNumIdle()); _pool.returnObject(obj1); assertEquals(1, _pool.getNumActive()); assertEquals(1, _pool.getNumIdle()); _pool.returnObject(obj0); assertEquals(0, _pool.getNumActive()); assertEquals(2, _pool.getNumIdle()); _pool.close(); } @Test public void testClose() throws Exception { @SuppressWarnings("resource") final ObjectPool pool = new TestObjectPool(); pool.close(); pool.close(); // should not error as of Pool 2.0. } // tests @Test public void testUnsupportedOperations() throws Exception { if (!getClass().equals(TestBaseObjectPool.class)) { return; // skip redundant tests } try (final ObjectPool pool = new TestObjectPool()) { assertTrue( pool.getNumIdle() < 0,"Negative expected."); assertTrue( pool.getNumActive() < 0,"Negative expected."); try { pool.clear(); fail("Expected UnsupportedOperationException"); } catch (final UnsupportedOperationException e) { // expected } try { pool.addObject(); fail("Expected UnsupportedOperationException"); } catch (final UnsupportedOperationException e) { // expected } } } } TestBasePoolableObjectFactory.java000066400000000000000000000055041405425132200352560ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/* * 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.commons.pool2; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.junit.jupiter.api.Test; /** */ public class TestBasePoolableObjectFactory { private static class TestFactory extends BasePooledObjectFactory { @Override public AtomicInteger create() throws Exception { return new AtomicInteger(0); } @Override public void destroyObject(final PooledObject p, final DestroyMode mode){ if (mode.equals(DestroyMode.ABANDONED)) { p.getObject().incrementAndGet(); } } @Override public PooledObject wrap(final AtomicInteger value) { return new DefaultPooledObject<>(value); } } @Test public void testDefaultMethods() throws Exception { final PooledObjectFactory factory = new TestFactory(); factory.activateObject(null); // a no-op factory.passivateObject(null); // a no-op factory.destroyObject(null); // a no-op assertTrue(factory.validateObject(null)); // constant true } /** * Default destroy does nothing to underlying AtomicInt, ABANDONED mode * increments the value. Verify that destroy with no mode does default, * destroy with ABANDONED mode increments. * * @throws Exception May occur in some failure modes */ @Test public void testDestroyModes() throws Exception { final PooledObjectFactory factory = new TestFactory(); final PooledObject pooledObj = factory.makeObject(); final AtomicInteger obj = pooledObj.getObject(); factory.destroyObject(pooledObj); assertEquals(0, obj.get()); factory.destroyObject(pooledObj, DestroyMode.ABANDONED); assertEquals(1, obj.get()); } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/TestKeyedObjectPool.java000066400000000000000000000741371405425132200333600ustar00rootroot00000000000000/* * 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.commons.pool2; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericKeyedObjectPool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; /** * Abstract test case for {@link ObjectPool} implementations. */ public abstract class TestKeyedObjectPool { protected static class FailingKeyedPooledObjectFactory implements KeyedPooledObjectFactory { private final List methodCalls = new ArrayList<>(); private int count = 0; private boolean makeObjectFail; private boolean activateObjectFail; private boolean validateObjectFail; private boolean passivateObjectFail; private boolean destroyObjectFail; public FailingKeyedPooledObjectFactory() { } @Override public void activateObject(final Object key, final PooledObject obj) throws Exception { methodCalls.add(new MethodCall("activateObject", key, obj.getObject())); if (activateObjectFail) { throw new PrivateException("activateObject"); } } @Override public void destroyObject(final Object key, final PooledObject obj) throws Exception { methodCalls.add(new MethodCall("destroyObject", key, obj.getObject())); if (destroyObjectFail) { throw new PrivateException("destroyObject"); } } public int getCurrentCount() { return count; } public List getMethodCalls() { return methodCalls; } public boolean isActivateObjectFail() { return activateObjectFail; } public boolean isDestroyObjectFail() { return destroyObjectFail; } public boolean isMakeObjectFail() { return makeObjectFail; } public boolean isPassivateObjectFail() { return passivateObjectFail; } public boolean isValidateObjectFail() { return validateObjectFail; } @Override public PooledObject makeObject(final Object key) throws Exception { final MethodCall call = new MethodCall("makeObject", key); methodCalls.add(call); final int originalCount = this.count++; if (makeObjectFail) { throw new PrivateException("makeObject"); } // Deliberate choice to create new object in case future unit test // checks for a specific object final Integer obj = Integer.valueOf(originalCount); call.setReturned(obj); return new DefaultPooledObject<>(obj); } @Override public void passivateObject(final Object key, final PooledObject obj) throws Exception { methodCalls.add(new MethodCall("passivateObject", key, obj.getObject())); if (passivateObjectFail) { throw new PrivateException("passivateObject"); } } public void reset() { count = 0; getMethodCalls().clear(); setMakeObjectFail(false); setActivateObjectFail(false); setValidateObjectFail(false); setPassivateObjectFail(false); setDestroyObjectFail(false); } public void setActivateObjectFail(final boolean activateObjectFail) { this.activateObjectFail = activateObjectFail; } public void setCurrentCount(final int count) { this.count = count; } public void setDestroyObjectFail(final boolean destroyObjectFail) { this.destroyObjectFail = destroyObjectFail; } public void setMakeObjectFail(final boolean makeObjectFail) { this.makeObjectFail = makeObjectFail; } public void setPassivateObjectFail(final boolean passivateObjectFail) { this.passivateObjectFail = passivateObjectFail; } public void setValidateObjectFail(final boolean validateObjectFail) { this.validateObjectFail = validateObjectFail; } @Override public boolean validateObject(final Object key, final PooledObject obj) { final MethodCall call = new MethodCall("validateObject", key, obj.getObject()); methodCalls.add(call); if (validateObjectFail) { throw new PrivateException("validateObject"); } final boolean r = true; call.returned(Boolean.valueOf(r)); return r; } } private static class TestFactory extends BaseKeyedPooledObjectFactory { @Override public Object create(final Object key) throws Exception { return new Object(); } @Override public PooledObject wrap(final Object value) { return new DefaultPooledObject<>(value); } } protected static final String KEY = "key"; private KeyedObjectPool _pool = null; // Deliberate choice to create a new object in case future unit tests check // for a specific object. private final Integer ZERO = Integer.valueOf(0); private final Integer ONE = Integer.valueOf(1); private void clear(final FailingKeyedPooledObjectFactory factory, final List expectedMethods) { factory.getMethodCalls().clear(); expectedMethods.clear(); } /** * Return what we expect to be the nth * object (zero indexed) created by the pool * for the given key. * @param key Key for the object to be obtained * @param n index of the object to be obtained * * @return the requested object */ protected abstract Object getNthObject(Object key, int n); protected abstract boolean isFifo(); protected abstract boolean isLifo(); /** * Create an {@link KeyedObjectPool} instance * that can contain at least minCapacity * idle and active objects, or * throw {@link IllegalArgumentException} * if such a pool cannot be created. * @param minCapacity Minimum capacity of the pool to create * * @return the newly created keyed object pool */ protected abstract KeyedObjectPool makeEmptyPool(int minCapacity); /** * Create an {@code KeyedObjectPool} with the specified factory. * The pool should be in a default configuration and conform to the expected * behaviors described in {@link KeyedObjectPool}. * Generally speaking there should be no limits on the various object counts. * * @param factory Factory to use to associate with the pool * @return The newly created empty pool */ protected abstract KeyedObjectPool makeEmptyPool( KeyedPooledObjectFactory factory); protected abstract Object makeKey(int n); private void reset(final KeyedObjectPool pool, final FailingKeyedPooledObjectFactory factory, final List expectedMethods) throws Exception { pool.clear(); clear(factory, expectedMethods); factory.reset(); } @AfterEach public void tearDown() throws Exception { _pool = null; } @Test public void testBaseAddObject() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object key = makeKey(0); try { assertEquals(0,_pool.getNumIdle()); assertEquals(0,_pool.getNumActive()); assertEquals(0,_pool.getNumIdle(key)); assertEquals(0,_pool.getNumActive(key)); _pool.addObject(key); assertEquals(1,_pool.getNumIdle()); assertEquals(0,_pool.getNumActive()); assertEquals(1,_pool.getNumIdle(key)); assertEquals(0,_pool.getNumActive(key)); final Object obj = _pool.borrowObject(key); assertEquals(getNthObject(key,0),obj); assertEquals(0,_pool.getNumIdle()); assertEquals(1,_pool.getNumActive()); assertEquals(0,_pool.getNumIdle(key)); assertEquals(1,_pool.getNumActive(key)); _pool.returnObject(key,obj); assertEquals(1,_pool.getNumIdle()); assertEquals(0,_pool.getNumActive()); assertEquals(1,_pool.getNumIdle(key)); assertEquals(0,_pool.getNumActive(key)); } catch(final UnsupportedOperationException e) { return; // skip this test if one of those calls is unsupported } finally { _pool.close(); } } @Test public void testBaseBorrow() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); final Object keyb = makeKey(1); assertEquals(getNthObject(keya,0),_pool.borrowObject(keya),"1"); assertEquals(getNthObject(keyb,0),_pool.borrowObject(keyb),"2"); assertEquals(getNthObject(keyb,1),_pool.borrowObject(keyb),"3"); assertEquals(getNthObject(keya,1),_pool.borrowObject(keya),"4"); assertEquals(getNthObject(keyb,2),_pool.borrowObject(keyb),"5"); assertEquals(getNthObject(keya,2),_pool.borrowObject(keya),"6"); _pool.close(); } @Test public void testBaseBorrowReturn() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); Object obj0 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,0),obj0); Object obj1 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,1),obj1); Object obj2 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,2),obj2); _pool.returnObject(keya,obj2); obj2 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,2),obj2); _pool.returnObject(keya,obj1); obj1 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,1),obj1); _pool.returnObject(keya,obj0); _pool.returnObject(keya,obj2); obj2 = _pool.borrowObject(keya); if (isLifo()) { assertEquals(getNthObject(keya,2),obj2); } if (isFifo()) { assertEquals(getNthObject(keya,0),obj2); } obj0 = _pool.borrowObject(keya); if (isLifo()) { assertEquals(getNthObject(keya,0),obj0); } if (isFifo()) { assertEquals(getNthObject(keya,2),obj0); } _pool.close(); } @Test public void testBaseClear() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); final Object obj0 = _pool.borrowObject(keya); final Object obj1 = _pool.borrowObject(keya); assertEquals(2,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); _pool.returnObject(keya,obj1); _pool.returnObject(keya,obj0); assertEquals(0,_pool.getNumActive(keya)); assertEquals(2,_pool.getNumIdle(keya)); _pool.clear(keya); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); final Object obj2 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,2),obj2); _pool.close(); } @Test public void testBaseInvalidateObject() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); final Object obj0 = _pool.borrowObject(keya); final Object obj1 = _pool.borrowObject(keya); assertEquals(2,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); _pool.invalidateObject(keya,obj0); assertEquals(1,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); _pool.invalidateObject(keya,obj1); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); _pool.close(); } @Test public void testBaseNumActiveNumIdle() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); final Object obj0 = _pool.borrowObject(keya); assertEquals(1,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); final Object obj1 = _pool.borrowObject(keya); assertEquals(2,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); _pool.returnObject(keya,obj1); assertEquals(1,_pool.getNumActive(keya)); assertEquals(1,_pool.getNumIdle(keya)); _pool.returnObject(keya,obj0); assertEquals(0,_pool.getNumActive(keya)); assertEquals(2,_pool.getNumIdle(keya)); assertEquals(0,_pool.getNumActive("xyzzy12345")); assertEquals(0,_pool.getNumIdle("xyzzy12345")); _pool.close(); } @Test public void testBaseNumActiveNumIdle2() throws Exception { try { _pool = makeEmptyPool(6); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); final Object keyb = makeKey(1); assertEquals(0,_pool.getNumActive()); assertEquals(0,_pool.getNumIdle()); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); assertEquals(0,_pool.getNumActive(keyb)); assertEquals(0,_pool.getNumIdle(keyb)); final Object objA0 = _pool.borrowObject(keya); final Object objB0 = _pool.borrowObject(keyb); assertEquals(2,_pool.getNumActive()); assertEquals(0,_pool.getNumIdle()); assertEquals(1,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); assertEquals(1,_pool.getNumActive(keyb)); assertEquals(0,_pool.getNumIdle(keyb)); final Object objA1 = _pool.borrowObject(keya); final Object objB1 = _pool.borrowObject(keyb); assertEquals(4,_pool.getNumActive()); assertEquals(0,_pool.getNumIdle()); assertEquals(2,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); assertEquals(2,_pool.getNumActive(keyb)); assertEquals(0,_pool.getNumIdle(keyb)); _pool.returnObject(keya,objA0); _pool.returnObject(keyb,objB0); assertEquals(2,_pool.getNumActive()); assertEquals(2,_pool.getNumIdle()); assertEquals(1,_pool.getNumActive(keya)); assertEquals(1,_pool.getNumIdle(keya)); assertEquals(1,_pool.getNumActive(keyb)); assertEquals(1,_pool.getNumIdle(keyb)); _pool.returnObject(keya,objA1); _pool.returnObject(keyb,objB1); assertEquals(0,_pool.getNumActive()); assertEquals(4,_pool.getNumIdle()); assertEquals(0,_pool.getNumActive(keya)); assertEquals(2,_pool.getNumIdle(keya)); assertEquals(0,_pool.getNumActive(keyb)); assertEquals(2,_pool.getNumIdle(keyb)); _pool.close(); } @Test public void testClosedPoolBehavior() throws Exception { final KeyedObjectPool pool; try { pool = makeEmptyPool(new TestFactory()); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final Object o1 = pool.borrowObject(KEY); final Object o2 = pool.borrowObject(KEY); pool.close(); try { pool.addObject(KEY); fail("A closed pool must throw an IllegalStateException when addObject is called."); } catch (final IllegalStateException ise) { // expected } try { pool.borrowObject(KEY); fail("A closed pool must throw an IllegalStateException when borrowObject is called."); } catch (final IllegalStateException ise) { // expected } // The following should not throw exceptions just because the pool is closed. assertEquals( 0, pool.getNumIdle(KEY),"A closed pool shouldn't have any idle objects."); assertEquals( 0, pool.getNumIdle(),"A closed pool shouldn't have any idle objects."); pool.getNumActive(); pool.getNumActive(KEY); pool.returnObject(KEY, o1); assertEquals( 0, pool.getNumIdle(KEY),"returnObject should not add items back into the idle object pool for a closed pool."); assertEquals( 0, pool.getNumIdle(),"returnObject should not add items back into the idle object pool for a closed pool."); pool.invalidateObject(KEY, o2); pool.clear(KEY); pool.clear(); pool.close(); } @Test public void testKPOFAddObjectUsage() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); final KeyedObjectPool pool; try { pool = makeEmptyPool(factory); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final List expectedMethods = new ArrayList<>(); // addObject should make a new object, passivate it and put it in the pool pool.addObject(KEY); expectedMethods.add(new MethodCall("makeObject", KEY).returned(ZERO)); expectedMethods.add(new MethodCall("passivateObject", KEY, ZERO)); assertEquals(expectedMethods, factory.getMethodCalls()); //// Test exception handling of addObject reset(pool, factory, expectedMethods); // makeObject Exceptions should be propagated to client code from addObject factory.setMakeObjectFail(true); try { pool.addObject(KEY); fail("Expected addObject to propagate makeObject exception."); } catch (final PrivateException pe) { // expected } expectedMethods.add(new MethodCall("makeObject", KEY)); assertEquals(expectedMethods, factory.getMethodCalls()); clear(factory, expectedMethods); // passivateObject Exceptions should be propagated to client code from addObject factory.setMakeObjectFail(false); factory.setPassivateObjectFail(true); try { pool.addObject(KEY); fail("Expected addObject to propagate passivateObject exception."); } catch (final PrivateException pe) { // expected } expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE)); expectedMethods.add(new MethodCall("passivateObject", KEY, ONE)); assertEquals(expectedMethods, factory.getMethodCalls()); pool.close(); } @Test public void testKPOFBorrowObjectUsages() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); final KeyedObjectPool pool; try { pool = makeEmptyPool(factory); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final List expectedMethods = new ArrayList<>(); Object obj; if (pool instanceof GenericKeyedObjectPool) { ((GenericKeyedObjectPool) pool).setTestOnBorrow(true); } /// Test correct behavior code paths // existing idle object should be activated and validated pool.addObject(KEY); clear(factory, expectedMethods); obj = pool.borrowObject(KEY); expectedMethods.add(new MethodCall("activateObject", KEY, ZERO)); expectedMethods.add(new MethodCall("validateObject", KEY, ZERO).returned(Boolean.TRUE)); assertEquals(expectedMethods, factory.getMethodCalls()); pool.returnObject(KEY, obj); //// Test exception handling of borrowObject reset(pool, factory, expectedMethods); // makeObject Exceptions should be propagated to client code from borrowObject factory.setMakeObjectFail(true); try { obj = pool.borrowObject(KEY); fail("Expected borrowObject to propagate makeObject exception."); } catch (final PrivateException pe) { // expected } expectedMethods.add(new MethodCall("makeObject", KEY)); assertEquals(expectedMethods, factory.getMethodCalls()); // when activateObject fails in borrowObject, a new object should be borrowed/created reset(pool, factory, expectedMethods); pool.addObject(KEY); clear(factory, expectedMethods); factory.setActivateObjectFail(true); expectedMethods.add(new MethodCall("activateObject", KEY, obj)); try { pool.borrowObject(KEY); fail("Expecting NoSuchElementException"); } catch (final NoSuchElementException e) { //Activate should fail } // After idle object fails validation, new on is created and activation // fails again for the new one. expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE)); expectedMethods.add(new MethodCall("activateObject", KEY, ONE)); TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here. assertEquals(expectedMethods, factory.getMethodCalls()); // when validateObject fails in borrowObject, a new object should be borrowed/created reset(pool, factory, expectedMethods); pool.addObject(KEY); clear(factory, expectedMethods); factory.setValidateObjectFail(true); // testOnBorrow is on, so this will throw when the newly created instance // fails validation try { pool.borrowObject(KEY); fail("Expecting NoSuchElementException"); } catch (final NoSuchElementException ex) { // expected } // Activate, then validate for idle instance expectedMethods.add(new MethodCall("activateObject", KEY, ZERO)); expectedMethods.add(new MethodCall("validateObject", KEY, ZERO)); // Make new instance, activate succeeds, validate fails expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE)); expectedMethods.add(new MethodCall("activateObject", KEY, ONE)); expectedMethods.add(new MethodCall("validateObject", KEY, ONE)); TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); assertEquals(expectedMethods, factory.getMethodCalls()); pool.close(); } @Test public void testKPOFClearUsages() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); final KeyedObjectPool pool; try { pool = makeEmptyPool(factory); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final List expectedMethods = new ArrayList<>(); /// Test correct behavior code paths pool.addObjects(KEY, 5); pool.clear(); //// Test exception handling clear should swallow destroy object failures reset(pool, factory, expectedMethods); factory.setDestroyObjectFail(true); pool.addObjects(KEY, 5); pool.clear(); pool.close(); } @Test public void testKPOFCloseUsages() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); KeyedObjectPool pool; try { pool = makeEmptyPool(factory); } catch (final UnsupportedOperationException uoe) { return; // test not supported } final List expectedMethods = new ArrayList<>(); /// Test correct behavior code paths pool.addObjects(KEY, 5); pool.close(); //// Test exception handling close should swallow failures try (final KeyedObjectPool pool2 = makeEmptyPool(factory)) { reset(pool2, factory, expectedMethods); factory.setDestroyObjectFail(true); pool2.addObjects(KEY, 5); } } @Test public void testKPOFInvalidateObjectUsages() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); final KeyedObjectPool pool; try { pool = makeEmptyPool(factory); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final List expectedMethods = new ArrayList<>(); Object obj; /// Test correct behavior code paths obj = pool.borrowObject(KEY); clear(factory, expectedMethods); // invalidated object should be destroyed pool.invalidateObject(KEY, obj); expectedMethods.add(new MethodCall("destroyObject", KEY, obj)); assertEquals(expectedMethods, factory.getMethodCalls()); //// Test exception handling of invalidateObject reset(pool, factory, expectedMethods); obj = pool.borrowObject(KEY); clear(factory, expectedMethods); factory.setDestroyObjectFail(true); try { pool.invalidateObject(KEY, obj); fail("Expecting destroy exception to propagate"); } catch (final PrivateException ex) { // Expected } Thread.sleep(250); // could be defered TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); assertEquals(expectedMethods, factory.getMethodCalls()); pool.close(); } @Test public void testKPOFReturnObjectUsages() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); final KeyedObjectPool pool; try { pool = makeEmptyPool(factory); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final List expectedMethods = new ArrayList<>(); Object obj; /// Test correct behavior code paths obj = pool.borrowObject(KEY); clear(factory, expectedMethods); // returned object should be passivated pool.returnObject(KEY, obj); expectedMethods.add(new MethodCall("passivateObject", KEY, obj)); assertEquals(expectedMethods, factory.getMethodCalls()); //// Test exception handling of returnObject reset(pool, factory, expectedMethods); // passivateObject should swallow exceptions and not add the object to the pool pool.addObject(KEY); pool.addObject(KEY); pool.addObject(KEY); assertEquals(3, pool.getNumIdle(KEY)); obj = pool.borrowObject(KEY); obj = pool.borrowObject(KEY); assertEquals(1, pool.getNumIdle(KEY)); assertEquals(2, pool.getNumActive(KEY)); clear(factory, expectedMethods); factory.setPassivateObjectFail(true); pool.returnObject(KEY, obj); expectedMethods.add(new MethodCall("passivateObject", KEY, obj)); TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here. assertEquals(expectedMethods, factory.getMethodCalls()); assertEquals(1, pool.getNumIdle(KEY)); // Not added assertEquals(1, pool.getNumActive(KEY)); // But not active reset(pool, factory, expectedMethods); obj = pool.borrowObject(KEY); clear(factory, expectedMethods); factory.setPassivateObjectFail(true); factory.setDestroyObjectFail(true); try { pool.returnObject(KEY, obj); if (!(pool instanceof GenericKeyedObjectPool)) { // ugh, 1.3-compat fail("Expecting destroyObject exception to be propagated"); } } catch (final PrivateException ex) { // Expected } pool.close(); } @Test public void testToString() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); try (final KeyedObjectPool pool = makeEmptyPool(factory)) { pool.toString(); } catch(final UnsupportedOperationException uoe) { return; // test not supported } } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/TestObjectPool.java000066400000000000000000000421411405425132200323640ustar00rootroot00000000000000/* * 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.commons.pool2; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.SoftReferenceObjectPool; import org.junit.jupiter.api.Test; /** * Abstract test case for {@link ObjectPool} implementations. */ public abstract class TestObjectPool { private static void clear(final MethodCallPoolableObjectFactory factory, final List expectedMethods) { factory.getMethodCalls().clear(); expectedMethods.clear(); } static void removeDestroyObjectCall(final List calls) { calls.removeIf(call -> "destroyObject".equals(call.getName())); } private static void reset(final ObjectPool pool, final MethodCallPoolableObjectFactory factory, final List expectedMethods) throws Exception { pool.clear(); clear(factory, expectedMethods); factory.reset(); } // Deliberate choice to create a new object in case future unit tests check // for a specific object. private final Integer ZERO = Integer.valueOf(0); private final Integer ONE = Integer.valueOf(1); /** * Create an {@code ObjectPool} with the specified factory. * The pool should be in a default configuration and conform to the expected * behaviors described in {@link ObjectPool}. * Generally speaking there should be no limits on the various object counts. * * @param factory The factory to be used by the object pool * * @return the newly created empty pool * * @throws UnsupportedOperationException if the pool being tested does not * follow pool contracts. */ protected abstract ObjectPool makeEmptyPool(PooledObjectFactory factory) throws UnsupportedOperationException; @Test public void testClosedPoolBehavior() throws Exception { final ObjectPool pool; try { pool = makeEmptyPool(new MethodCallPoolableObjectFactory()); } catch (final UnsupportedOperationException uoe) { return; // test not supported } final Object o1 = pool.borrowObject(); final Object o2 = pool.borrowObject(); pool.close(); try { pool.addObject(); fail("A closed pool must throw an IllegalStateException when addObject is called."); } catch (final IllegalStateException ise) { // expected } try { pool.borrowObject(); fail("A closed pool must throw an IllegalStateException when borrowObject is called."); } catch (final IllegalStateException ise) { // expected } // The following should not throw exceptions just because the pool is closed. if (pool.getNumIdle() >= 0) { assertEquals( 0, pool.getNumIdle(),"A closed pool shouldn't have any idle objects."); } if (pool.getNumActive() >= 0) { assertEquals( 2, pool.getNumActive(),"A closed pool should still keep count of active objects."); } pool.returnObject(o1); if (pool.getNumIdle() >= 0) { assertEquals( 0, pool.getNumIdle(),"returnObject should not add items back into the idle object pool for a closed pool."); } if (pool.getNumActive() >= 0) { assertEquals( 1, pool.getNumActive(),"A closed pool should still keep count of active objects."); } pool.invalidateObject(o2); if (pool.getNumIdle() >= 0) { assertEquals( 0, pool.getNumIdle(),"invalidateObject must not add items back into the idle object pool."); } if (pool.getNumActive() >= 0) { assertEquals( 0, pool.getNumActive(),"A closed pool should still keep count of active objects."); } pool.clear(); pool.close(); } @Test public void testPOFAddObjectUsage() throws Exception { final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory(); final ObjectPool pool; try { pool = makeEmptyPool(factory); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final List expectedMethods = new ArrayList<>(); assertEquals(0, pool.getNumActive()); assertEquals(0, pool.getNumIdle()); // addObject should make a new object, passivate it and put it in the pool pool.addObject(); assertEquals(0, pool.getNumActive()); assertEquals(1, pool.getNumIdle()); expectedMethods.add(new MethodCall("makeObject").returned(ZERO)); // StackObjectPool, SoftReferenceObjectPool also validate on add if (pool instanceof SoftReferenceObjectPool) { expectedMethods.add(new MethodCall( "validateObject", ZERO).returned(Boolean.TRUE)); } expectedMethods.add(new MethodCall("passivateObject", ZERO)); assertEquals(expectedMethods, factory.getMethodCalls()); //// Test exception handling of addObject reset(pool, factory, expectedMethods); // makeObject Exceptions should be propagated to client code from addObject factory.setMakeObjectFail(true); try { pool.addObject(); fail("Expected addObject to propagate makeObject exception."); } catch (final PrivateException pe) { // expected } expectedMethods.add(new MethodCall("makeObject")); assertEquals(expectedMethods, factory.getMethodCalls()); clear(factory, expectedMethods); // passivateObject Exceptions should be propagated to client code from addObject factory.setMakeObjectFail(false); factory.setPassivateObjectFail(true); try { pool.addObject(); fail("Expected addObject to propagate passivateObject exception."); } catch (final PrivateException pe) { // expected } expectedMethods.add(new MethodCall("makeObject").returned(ONE)); // StackObjectPool, SofReferenceObjectPool also validate on add if (pool instanceof SoftReferenceObjectPool) { expectedMethods.add(new MethodCall( "validateObject", ONE).returned(Boolean.TRUE)); } expectedMethods.add(new MethodCall("passivateObject", ONE)); assertEquals(expectedMethods, factory.getMethodCalls()); pool.close(); } @Test public void testPOFBorrowObjectUsages() throws Exception { final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory(); final ObjectPool pool; try { pool = makeEmptyPool(factory); } catch (final UnsupportedOperationException uoe) { return; // test not supported } if (pool instanceof GenericObjectPool) { ((GenericObjectPool) pool).setTestOnBorrow(true); } final List expectedMethods = new ArrayList<>(); Object obj; /// Test correct behavior code paths // existing idle object should be activated and validated pool.addObject(); clear(factory, expectedMethods); obj = pool.borrowObject(); expectedMethods.add(new MethodCall("activateObject", ZERO)); expectedMethods.add(new MethodCall("validateObject", ZERO).returned(Boolean.TRUE)); assertEquals(expectedMethods, factory.getMethodCalls()); pool.returnObject(obj); //// Test exception handling of borrowObject reset(pool, factory, expectedMethods); // makeObject Exceptions should be propagated to client code from borrowObject factory.setMakeObjectFail(true); try { obj = pool.borrowObject(); fail("Expected borrowObject to propagate makeObject exception."); } catch (final PrivateException pe) { // expected } expectedMethods.add(new MethodCall("makeObject")); assertEquals(expectedMethods, factory.getMethodCalls()); // when activateObject fails in borrowObject, a new object should be borrowed/created reset(pool, factory, expectedMethods); pool.addObject(); clear(factory, expectedMethods); factory.setActivateObjectFail(true); expectedMethods.add(new MethodCall("activateObject", obj)); try { pool.borrowObject(); fail("Expecting NoSuchElementException"); } catch (final NoSuchElementException ex) { // Expected - newly created object will also fail to activate } // Idle object fails activation, new one created, also fails expectedMethods.add(new MethodCall("makeObject").returned(ONE)); expectedMethods.add(new MethodCall("activateObject", ONE)); removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here. assertEquals(expectedMethods, factory.getMethodCalls()); // when validateObject fails in borrowObject, a new object should be borrowed/created reset(pool, factory, expectedMethods); pool.addObject(); clear(factory, expectedMethods); factory.setValidateObjectFail(true); expectedMethods.add(new MethodCall("activateObject", ZERO)); expectedMethods.add(new MethodCall("validateObject", ZERO)); try { pool.borrowObject(); } catch (final NoSuchElementException ex) { // Expected - newly created object will also fail to validate } // Idle object is activated, but fails validation. // New instance is created, activated and then fails validation expectedMethods.add(new MethodCall("makeObject").returned(ONE)); expectedMethods.add(new MethodCall("activateObject", ONE)); expectedMethods.add(new MethodCall("validateObject", ONE)); removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here. // Second activate and validate are missing from expectedMethods assertTrue(factory.getMethodCalls().containsAll(expectedMethods)); pool.close(); } @Test public void testPOFClearUsages() throws Exception { final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory(); final ObjectPool pool; try { pool = makeEmptyPool(factory); } catch (final UnsupportedOperationException uoe) { return; // test not supported } final List expectedMethods = new ArrayList<>(); /// Test correct behavior code paths pool.addObjects(5); pool.clear(); //// Test exception handling clear should swallow destroy object failures reset(pool, factory, expectedMethods); factory.setDestroyObjectFail(true); pool.addObjects(5); pool.clear(); pool.close(); } @Test public void testPOFCloseUsages() throws Exception { final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory(); ObjectPool pool; try { pool = makeEmptyPool(factory); } catch (final UnsupportedOperationException uoe) { return; // test not supported } final List expectedMethods = new ArrayList<>(); /// Test correct behavior code paths pool.addObjects(5); pool.close(); //// Test exception handling close should swallow failures try { pool = makeEmptyPool(factory); } catch (final UnsupportedOperationException uoe) { return; // test not supported } reset(pool, factory, expectedMethods); factory.setDestroyObjectFail(true); pool.addObjects(5); pool.close(); } @Test public void testPOFInvalidateObjectUsages() throws Exception { final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory(); final ObjectPool pool; try { pool = makeEmptyPool(factory); } catch (final UnsupportedOperationException uoe) { return; // test not supported } final List expectedMethods = new ArrayList<>(); Object obj; /// Test correct behavior code paths obj = pool.borrowObject(); clear(factory, expectedMethods); // invalidated object should be destroyed pool.invalidateObject(obj); expectedMethods.add(new MethodCall("destroyObject", obj)); assertEquals(expectedMethods, factory.getMethodCalls()); //// Test exception handling of invalidateObject reset(pool, factory, expectedMethods); obj = pool.borrowObject(); clear(factory, expectedMethods); factory.setDestroyObjectFail(true); try { pool.invalidateObject(obj); fail("Expecting destroy exception to propagate"); } catch (final PrivateException ex) { // Expected } Thread.sleep(250); // could be deferred removeDestroyObjectCall(factory.getMethodCalls()); assertEquals(expectedMethods, factory.getMethodCalls()); pool.close(); } @Test public void testPOFReturnObjectUsages() throws Exception { final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory(); final ObjectPool pool; try { pool = makeEmptyPool(factory); } catch (final UnsupportedOperationException uoe) { return; // test not supported } final List expectedMethods = new ArrayList<>(); Object obj; /// Test correct behavior code paths obj = pool.borrowObject(); clear(factory, expectedMethods); // returned object should be passivated pool.returnObject(obj); // StackObjectPool, SoftReferenceObjectPool also validate on return if (pool instanceof SoftReferenceObjectPool) { expectedMethods.add(new MethodCall( "validateObject", obj).returned(Boolean.TRUE)); } expectedMethods.add(new MethodCall("passivateObject", obj)); assertEquals(expectedMethods, factory.getMethodCalls()); //// Test exception handling of returnObject reset(pool, factory, expectedMethods); pool.addObject(); pool.addObject(); pool.addObject(); assertEquals(3, pool.getNumIdle()); // passivateObject should swallow exceptions and not add the object to the pool obj = pool.borrowObject(); pool.borrowObject(); assertEquals(1, pool.getNumIdle()); assertEquals(2, pool.getNumActive()); clear(factory, expectedMethods); factory.setPassivateObjectFail(true); pool.returnObject(obj); // StackObjectPool, SoftReferenceObjectPool also validate on return if (pool instanceof SoftReferenceObjectPool) { expectedMethods.add(new MethodCall( "validateObject", obj).returned(Boolean.TRUE)); } expectedMethods.add(new MethodCall("passivateObject", obj)); removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here. assertEquals(expectedMethods, factory.getMethodCalls()); assertEquals(1, pool.getNumIdle()); // Not returned assertEquals(1, pool.getNumActive()); // But not in active count // destroyObject should swallow exceptions too reset(pool, factory, expectedMethods); obj = pool.borrowObject(); clear(factory, expectedMethods); factory.setPassivateObjectFail(true); factory.setDestroyObjectFail(true); pool.returnObject(obj); pool.close(); } @Test public void testToString() throws Exception { final ObjectPool pool; try { pool = makeEmptyPool(new MethodCallPoolableObjectFactory()); } catch (final UnsupportedOperationException uoe) { return; // test not supported } pool.toString(); pool.close(); } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/TestPoolUtils.java000066400000000000000000001102571405425132200322620ustar00rootroot00000000000000/* * 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.commons.pool2; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TimerTask; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericKeyedObjectPool; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.TestGenericKeyedObjectPool; import org.junit.jupiter.api.Test; import org.opentest4j.AssertionFailedError; /** * Unit tests for {@link PoolUtils}. * * TODO Replace our own mocking with a mocking library like Mockito. */ public class TestPoolUtils { private static class MethodCallLogger implements InvocationHandler { private final List calledMethods; MethodCallLogger(final List calledMethods) { this.calledMethods = calledMethods; } @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { if (calledMethods == null) { return null; } calledMethods.add(method.getName()); if (boolean.class.equals(method.getReturnType())) { return Boolean.FALSE; } if (int.class.equals(method.getReturnType())) { return Integer.valueOf(0); } if (long.class.equals(method.getReturnType())) { return Long.valueOf(0); } if (Object.class.equals(method.getReturnType())) { return new Object(); } if (PooledObject.class.equals(method.getReturnType())) { return new DefaultPooledObject<>(new Object()); } return null; } } /** Period between checks for minIdle tests. Increase this if you happen to get too many false failures. */ private static final int CHECK_PERIOD = 300; /** Times to let the minIdle check run. */ private static final int CHECK_COUNT = 4; /** Sleep time to let the minIdle tests run CHECK_COUNT times. */ private static final int CHECK_SLEEP_PERIOD = CHECK_PERIOD * (CHECK_COUNT - 1) + CHECK_PERIOD / 2; private static T createProxy(final Class clazz, final InvocationHandler handler) { @SuppressWarnings("unchecked") final T ret = (T) Proxy.newProxyInstance( clazz.getClassLoader(), new Class[] { clazz }, handler); return ret; } private static T createProxy(final Class clazz, final List logger) { return createProxy(clazz, new MethodCallLogger(logger)); } private static List invokeEveryMethod(final KeyedObjectPool kop) throws Exception { kop.addObject(null); kop.borrowObject(null); kop.clear(); kop.clear(null); kop.close(); kop.getNumActive(); kop.getNumActive(null); kop.getNumIdle(); kop.getNumIdle(null); kop.invalidateObject(null, new Object()); kop.returnObject(null, new Object()); kop.toString(); final List expectedMethods = Arrays.asList("addObject", "borrowObject", "clear", "clear", "close", "getNumActive", "getNumActive", "getNumIdle", "getNumIdle", "invalidateObject", "returnObject", "toString"); return expectedMethods; } private static List invokeEveryMethod(final KeyedPooledObjectFactory kpof) throws Exception { kpof.activateObject(null, null); kpof.destroyObject(null, null); kpof.makeObject(null); kpof.passivateObject(null, null); kpof.validateObject(null, null); kpof.toString(); final List expectedMethods = Arrays.asList("activateObject", "destroyObject", "makeObject", "passivateObject", "validateObject", "toString"); return expectedMethods; } private static List invokeEveryMethod(final ObjectPool op) throws Exception { op.addObject(); op.borrowObject(); op.clear(); op.close(); op.getNumActive(); op.getNumIdle(); op.invalidateObject(new Object()); op.returnObject(new Object()); op.toString(); final List expectedMethods = Arrays.asList("addObject", "borrowObject", "clear", "close", "getNumActive", "getNumIdle", "invalidateObject", "returnObject", "toString"); return expectedMethods; } private static List invokeEveryMethod(final PooledObjectFactory pof) throws Exception { pof.activateObject(null); pof.destroyObject(null); pof.makeObject(); pof.passivateObject(null); pof.validateObject(null); pof.toString(); final List expectedMethods = Arrays.asList("activateObject", "destroyObject", "makeObject", "passivateObject", "validateObject", "toString"); return expectedMethods; } @Test public void testCheckMinIdleKeyedObjectPool() throws Exception { try { PoolUtils.checkMinIdle(null, new Object(), 1, 1); fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must not allow null pool."); } catch (final IllegalArgumentException iae) { // expected } try (@SuppressWarnings("unchecked") final KeyedObjectPool pool = createProxy(KeyedObjectPool.class, (List)null)) { PoolUtils.checkMinIdle(pool, (Object)null, 1, 1); fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must not accept null keys."); } catch (final IllegalArgumentException iae) { // expected } try (@SuppressWarnings("unchecked") final KeyedObjectPool pool = createProxy(KeyedObjectPool.class, (List)null)) { PoolUtils.checkMinIdle(pool, new Object(), -1, 1); fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must not accept negative min idle values."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); final Object key = new Object(); // Test that the minIdle check doesn't add too many idle objects @SuppressWarnings("unchecked") final KeyedPooledObjectFactory kpof = createProxy(KeyedPooledObjectFactory.class, calledMethods); try (final KeyedObjectPool kop = new GenericKeyedObjectPool<>(kpof)) { PoolUtils.checkMinIdle(kop, key, 2, 100); Thread.sleep(400); assertEquals(2, kop.getNumIdle(key)); assertEquals(2, kop.getNumIdle()); } int makeObjectCount = 0; final Iterator iter = calledMethods.iterator(); while (iter.hasNext()) { final String methodName = iter.next(); if ("makeObject".equals(methodName)) { makeObjectCount++; } } assertEquals( 2, makeObjectCount,"makeObject should have been called two time"); // Because this isn't deterministic and you can get false failures, try more than once. AssertionFailedError afe = null; int triesLeft = 3; do { afe = null; try { calledMethods.clear(); try (@SuppressWarnings("unchecked") final KeyedObjectPool pool = createProxy(KeyedObjectPool.class, calledMethods)) { // checks minIdle immediately final TimerTask task = PoolUtils.checkMinIdle(pool, key, 1, CHECK_PERIOD); Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times. task.cancel(); task.toString(); final List expectedMethods = new ArrayList<>(); for (int i = 0; i < CHECK_COUNT; i++) { expectedMethods.add("getNumIdle"); expectedMethods.add("addObject"); } expectedMethods.add("toString"); assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler } } catch (final AssertionFailedError e) { afe = e; } } while (--triesLeft > 0 && afe != null); if (afe != null) { throw afe; } } @Test public void testCheckMinIdleKeyedObjectPoolKeys() throws Exception { // Because this isn't deterministic and you can get false failures, try more than once. AssertionFailedError afe = null; int triesLeft = 3; do { afe = null; final List calledMethods = new ArrayList<>(); try (@SuppressWarnings("unchecked") final KeyedObjectPool pool = createProxy(KeyedObjectPool.class, calledMethods)) { final Collection keys = new ArrayList<>(2); keys.add("one"); keys.add("two"); // checks minIdle immediately final Map tasks = PoolUtils.checkMinIdle(pool, keys, 1, CHECK_PERIOD); Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times. for (final TimerTask task : tasks.values()) { task.cancel(); } final List expectedMethods = new ArrayList<>(); for (int i = 0; i < CHECK_COUNT * keys.size(); i++) { expectedMethods.add("getNumIdle"); expectedMethods.add("addObject"); } assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler } catch (final AssertionFailedError e) { afe = e; } } while (--triesLeft > 0 && afe != null); if (afe != null) { throw afe; } } @Test public void testCheckMinIdleKeyedObjectPoolKeysNulls() throws Exception { try (@SuppressWarnings("unchecked") final KeyedObjectPool pool = createProxy(KeyedObjectPool.class, (List)null)) { PoolUtils.checkMinIdle(pool, (Collection) null, 1, 1); fail("PoolUtils.checkMinIdle(KeyedObjectPool,Collection,int,long) must not accept null keys."); } catch (final IllegalArgumentException iae) { // expected } try (@SuppressWarnings("unchecked") final KeyedObjectPool pool = createProxy(KeyedObjectPool.class, (List)null)) { PoolUtils.checkMinIdle(pool, (Collection) Collections.emptyList(), 1, 1); } catch (final IllegalArgumentException iae) { fail("PoolUtils.checkMinIdle(KeyedObjectPool,Collection,int,long) must accept empty lists."); } } @Test public void testCheckMinIdleObjectPool() throws Exception { try { PoolUtils.checkMinIdle(null, 1, 1); fail("PoolUtils.checkMinIdle(ObjectPool,,) must not allow null pool."); } catch (final IllegalArgumentException iae) { // expected } try (@SuppressWarnings("unchecked") final ObjectPool pool = createProxy(ObjectPool.class, (List) null)) { PoolUtils.checkMinIdle(pool, -1, 1); fail("PoolUtils.checkMinIdle(ObjectPool,,) must not accept negative min idle values."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); // Test that the minIdle check doesn't add too many idle objects @SuppressWarnings("unchecked") final PooledObjectFactory pof = createProxy(PooledObjectFactory.class, calledMethods); try (final ObjectPool op = new GenericObjectPool<>(pof)) { PoolUtils.checkMinIdle(op, 2, 100); Thread.sleep(1000); assertEquals(2, op.getNumIdle()); } int makeObjectCount = 0; final Iterator iter = calledMethods.iterator(); while (iter.hasNext()) { final String methodName = iter.next(); if ("makeObject".equals(methodName)) { makeObjectCount++; } } assertEquals( 2, makeObjectCount,"makeObject should have been called two time"); // Because this isn't deterministic and you can get false failures, try more than once. AssertionFailedError afe = null; int triesLeft = 3; do { afe = null; try { calledMethods.clear(); try (@SuppressWarnings("unchecked") final ObjectPool pool = createProxy(ObjectPool.class, calledMethods)) { final TimerTask task = PoolUtils.checkMinIdle(pool, 1, CHECK_PERIOD); // checks minIdle immediately Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times. task.cancel(); task.toString(); final List expectedMethods = new ArrayList<>(); for (int i = 0; i < CHECK_COUNT; i++) { expectedMethods.add("getNumIdle"); expectedMethods.add("addObject"); } expectedMethods.add("toString"); assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler } } catch (final AssertionFailedError e) { afe = e; } } while (--triesLeft > 0 && afe != null); if (afe != null) { throw afe; } } @Test public void testCheckRethrow() { try { PoolUtils.checkRethrow(new Exception()); } catch (final Throwable t) { fail("PoolUtils.checkRethrow(Throwable) must rethrow only ThreadDeath and VirtualMachineError."); } try { PoolUtils.checkRethrow(new ThreadDeath()); fail("PoolUtils.checkRethrow(Throwable) must rethrow ThreadDeath."); } catch (final ThreadDeath td) { // expected } catch (final Throwable t) { fail("PoolUtils.checkRethrow(Throwable) must rethrow only ThreadDeath and VirtualMachineError."); } try { PoolUtils.checkRethrow(new InternalError()); // InternalError extends VirtualMachineError fail("PoolUtils.checkRethrow(Throwable) must rethrow VirtualMachineError."); } catch (final VirtualMachineError td) { // expected } catch (final Throwable t) { fail("PoolUtils.checkRethrow(Throwable) must rethrow only ThreadDeath and VirtualMachineError."); } } @Test public void testErodingObjectPoolDefaultFactor() { try (@SuppressWarnings("unchecked") final ObjectPool internalPool = createProxy(ObjectPool.class, (arg0, arg1, arg2) -> null); final ObjectPool pool = PoolUtils.erodingPool(internalPool)) { final String expectedToString = "ErodingObjectPool{factor=ErodingFactor{factor=1.0, idleHighWaterMark=1}, pool=" + internalPool + "}"; // The factor is not exposed, but will be printed in the toString() method // In this case since we didn't pass one, the default 1.0f will be printed assertEquals(expectedToString, pool.toString()); } } @Test public void testErodingPerKeyKeyedObjectPool() throws Exception { try (final KeyedObjectPool erodingPool = PoolUtils .erodingPool((KeyedObjectPool) null, 1f, true)) { fail("PoolUtils.erodingPool(KeyedObjectPool) must not allow a null pool."); } catch (final IllegalArgumentException iae) { // expected } try (final KeyedObjectPool erodingPool = PoolUtils .erodingPool((KeyedObjectPool) null, 0f, true)) { fail("PoolUtils.erodingPool(ObjectPool, float, boolean) must not allow a non-positive factor."); } catch (final IllegalArgumentException iae) { // expected } try (final KeyedObjectPool erodingPool = PoolUtils .erodingPool((KeyedObjectPool) null, 1f, true)) { fail("PoolUtils.erodingPool(KeyedObjectPool, float, boolean) must not allow a null pool."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); final InvocationHandler handler = new MethodCallLogger(calledMethods) { @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { Object o = super.invoke(proxy, method, args); if (o instanceof Integer) { // so getNumActive/getNumIdle are not zero. o = Integer.valueOf(1); } return o; } }; // If the logic behind PoolUtils.erodingPool changes then this will need to be tweaked. final float factor = 0.01f; // about ~9 seconds until first discard try (@SuppressWarnings("unchecked") final KeyedObjectPool pool = PoolUtils.erodingPool(createProxy(KeyedObjectPool.class, handler), factor, true)) { final List expectedMethods = new ArrayList<>(); assertEquals(expectedMethods, calledMethods); final Object key = "key"; Object o = pool.borrowObject(key); expectedMethods.add("borrowObject"); assertEquals(expectedMethods, calledMethods); pool.returnObject(key, o); expectedMethods.add("returnObject"); assertEquals(expectedMethods, calledMethods); for (int i = 0; i < 5; i++) { o = pool.borrowObject(key); expectedMethods.add("borrowObject"); Thread.sleep(50); pool.returnObject(key, o); expectedMethods.add("returnObject"); assertEquals(expectedMethods, calledMethods); expectedMethods.clear(); calledMethods.clear(); } Thread.sleep(10000); // 10 seconds o = pool.borrowObject(key); expectedMethods.add("borrowObject"); pool.returnObject(key, o); expectedMethods.add("getNumIdle"); expectedMethods.add("invalidateObject"); assertEquals(expectedMethods, calledMethods); final String expectedToString = "ErodingPerKeyKeyedObjectPool{factor=" + factor + ", keyedPool=null}"; assertEquals(expectedToString, pool.toString()); } } @Test public void testErodingPoolKeyedObjectPool() throws Exception { try (final KeyedObjectPool erodingPool = PoolUtils .erodingPool((KeyedObjectPool) null)) { fail("PoolUtils.erodingPool(KeyedObjectPool) must not allow a null pool."); } catch (final IllegalArgumentException iae) { // expected } try (final KeyedObjectPool erodingPool = PoolUtils .erodingPool((KeyedObjectPool) null, 1f)) { fail("PoolUtils.erodingPool(KeyedObjectPool, float) must not allow a null pool."); } catch (final IllegalArgumentException iae) { // expected } try (final KeyedObjectPool erodingPool = PoolUtils .erodingPool((KeyedObjectPool) null, 1f, true)) { fail("PoolUtils.erodingPool(KeyedObjectPool, float, boolean) must not allow a null pool."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); final InvocationHandler handler = new MethodCallLogger(calledMethods) { @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { Object o = super.invoke(proxy, method, args); if (o instanceof Integer) { // so getNumActive/getNumIdle are not zero. o = Integer.valueOf(1); } return o; } }; try (@SuppressWarnings({ "unchecked" }) final KeyedObjectPool o = PoolUtils.erodingPool(createProxy(KeyedObjectPool.class, handler), 0f)) { fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor."); } catch (final IllegalArgumentException iae) { // expected } try (@SuppressWarnings({ "unchecked" }) final KeyedObjectPool o = PoolUtils.erodingPool(createProxy(KeyedObjectPool.class, handler), 0f, false)) { fail("PoolUtils.erodingPool(ObjectPool, float, boolean) must not allow a non-positive factor."); } catch (final IllegalArgumentException iae) { // expected } // If the logic behind PoolUtils.erodingPool changes then this will need to be tweaked. final float factor = 0.01f; // about ~9 seconds until first discard final List expectedMethods = new ArrayList<>(); try (@SuppressWarnings("unchecked") final KeyedObjectPool pool = PoolUtils.erodingPool(createProxy(KeyedObjectPool.class, handler), factor)) { assertEquals(expectedMethods, calledMethods); final Object key = "key"; pool.addObject(key); expectedMethods.add("addObject"); Object o = pool.borrowObject(key); expectedMethods.add("borrowObject"); assertEquals(expectedMethods, calledMethods); pool.returnObject(key, o); expectedMethods.add("returnObject"); assertEquals(expectedMethods, calledMethods); // the invocation handler always returns 1 assertEquals(1, pool.getNumActive()); expectedMethods.add("getNumActive"); assertEquals(1, pool.getNumIdle()); expectedMethods.add("getNumIdle"); for (int i = 0; i < 5; i++) { o = pool.borrowObject(key); expectedMethods.add("borrowObject"); Thread.sleep(50); pool.returnObject(key, o); expectedMethods.add("returnObject"); assertEquals(expectedMethods, calledMethods); expectedMethods.clear(); calledMethods.clear(); } Thread.sleep(10000); // 10 seconds o = pool.borrowObject(key); expectedMethods.add("borrowObject"); pool.returnObject(key, o); expectedMethods.add("getNumIdle"); expectedMethods.add("invalidateObject"); pool.clear(); } expectedMethods.add("clear"); expectedMethods.add("close"); assertEquals(expectedMethods, calledMethods); } @Test public void testErodingPoolKeyedObjectPoolDefaultFactor() { try (@SuppressWarnings("unchecked") final KeyedObjectPool internalPool = createProxy(KeyedObjectPool.class, (arg0, arg1, arg2) -> null); final KeyedObjectPool pool = PoolUtils.erodingPool(internalPool)) { final String expectedToString = "ErodingKeyedObjectPool{factor=ErodingFactor{factor=1.0, idleHighWaterMark=1}, keyedPool=" + internalPool + "}"; // The factor is not exposed, but will be printed in the toString() method // In this case since we didn't pass one, the default 1.0f will be printed assertEquals(expectedToString, pool.toString()); } } @Test public void testErodingPoolObjectPool() throws Exception { try (final ObjectPool erodingPool = PoolUtils.erodingPool((ObjectPool) null)) { fail("PoolUtils.erodingPool(ObjectPool) must not allow a null pool."); } catch (final IllegalArgumentException iae) { // expected } try (final ObjectPool erodingPool = PoolUtils.erodingPool((ObjectPool) null, 1f)) { fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a null pool."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); final InvocationHandler handler = new MethodCallLogger(calledMethods) { @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { Object o = super.invoke(proxy, method, args); if (o instanceof Integer) { // so getNumActive/getNumIdle are not zero. o = Integer.valueOf(1); } return o; } }; try (@SuppressWarnings({ "unchecked" }) final ObjectPool o = PoolUtils.erodingPool(createProxy(ObjectPool.class, handler), -1f)) { fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor."); } catch (final IllegalArgumentException iae) { // expected } // If the logic behind PoolUtils.erodingPool changes then this will need to be tweaked. final float factor = 0.01f; // about ~9 seconds until first discard final List expectedMethods = new ArrayList<>(); try (@SuppressWarnings("unchecked") final ObjectPool pool = PoolUtils.erodingPool(createProxy(ObjectPool.class, handler), factor)) { assertEquals(expectedMethods, calledMethods); pool.addObject(); expectedMethods.add("addObject"); Object o = pool.borrowObject(); expectedMethods.add("borrowObject"); assertEquals(expectedMethods, calledMethods); pool.returnObject(o); expectedMethods.add("returnObject"); assertEquals(expectedMethods, calledMethods); // the invocation handler always returns 1 assertEquals(1, pool.getNumActive()); expectedMethods.add("getNumActive"); assertEquals(1, pool.getNumIdle()); expectedMethods.add("getNumIdle"); for (int i = 0; i < 5; i++) { o = pool.borrowObject(); expectedMethods.add("borrowObject"); Thread.sleep(50); pool.returnObject(o); expectedMethods.add("returnObject"); assertEquals(expectedMethods, calledMethods); expectedMethods.clear(); calledMethods.clear(); } Thread.sleep(10000); // 10 seconds o = pool.borrowObject(); expectedMethods.add("borrowObject"); pool.returnObject(o); expectedMethods.add("getNumIdle"); expectedMethods.add("invalidateObject"); pool.clear(); } expectedMethods.add("clear"); expectedMethods.add("close"); assertEquals(expectedMethods, calledMethods); } @Test public void testJavaBeanInstantiation() { assertNotNull(new PoolUtils()); } @SuppressWarnings("deprecation") @Test public void testPrefillKeyedObjectPool() throws Exception { try { PoolUtils.prefill(null, new Object(), 1); fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null pool."); } catch (final IllegalArgumentException iae) { // expected } try (final KeyedObjectPool pool = new GenericKeyedObjectPool<>( new TestGenericKeyedObjectPool.SimpleFactory<>())) { PoolUtils.prefill(pool, (Object) null, 1); fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null key."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); try (@SuppressWarnings("unchecked") final KeyedObjectPool pool = createProxy(KeyedObjectPool.class, calledMethods)) { PoolUtils.prefill(pool, new Object(), 0); final List expectedMethods = new ArrayList<>(); expectedMethods.add("addObjects"); assertEquals(expectedMethods, calledMethods); calledMethods.clear(); PoolUtils.prefill(pool, new Object(), 3); assertEquals(expectedMethods, calledMethods); } } @SuppressWarnings("deprecation") @Test public void testPrefillKeyedObjectPoolCollection() throws Exception { try (@SuppressWarnings("unchecked") final KeyedObjectPool pool = createProxy(KeyedObjectPool.class, (List) null)) { PoolUtils.prefill(pool, (Collection) null, 1); fail("PoolUtils.prefill(KeyedObjectPool,Collection,int) must not accept null keys."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); try (@SuppressWarnings("unchecked") final KeyedObjectPool pool = createProxy(KeyedObjectPool.class, calledMethods)) { final Set keys = new HashSet<>(); PoolUtils.prefill(pool, keys, 0); final List expectedMethods = new ArrayList<>(); expectedMethods.add("addObjects"); assertEquals(expectedMethods, calledMethods); calledMethods.clear(); keys.add("one"); keys.add("two"); keys.add("three"); final int count = 3; PoolUtils.prefill(pool, keys, count); assertEquals(expectedMethods, calledMethods); } } @SuppressWarnings("deprecation") @Test public void testPrefillObjectPool() throws Exception { try { PoolUtils.prefill(null, 1); fail("PoolUtils.prefill(ObjectPool,int) must not allow null pool."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); try (@SuppressWarnings("unchecked") final ObjectPool pool = createProxy(ObjectPool.class, calledMethods)) { PoolUtils.prefill(pool, 0); final List expectedMethods = new ArrayList<>(); expectedMethods.add("addObjects"); assertEquals(expectedMethods, calledMethods); calledMethods.clear(); PoolUtils.prefill(pool, 3); assertEquals(expectedMethods, calledMethods); } } @Test public void testSynchronizedPoolableFactoryKeyedPoolableObjectFactory() throws Exception { try { PoolUtils.synchronizedKeyedPooledFactory((KeyedPooledObjectFactory) null); fail("PoolUtils.synchronizedPoolableFactory(KeyedPoolableObjectFactory) must not allow a null factory."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); @SuppressWarnings("unchecked") final KeyedPooledObjectFactory kpof = createProxy(KeyedPooledObjectFactory.class, calledMethods); final KeyedPooledObjectFactory skpof = PoolUtils.synchronizedKeyedPooledFactory(kpof); final List expectedMethods = invokeEveryMethod(skpof); assertEquals(expectedMethods, calledMethods); // TODO: Anyone feel motivated to construct a test that verifies proper synchronization? } @Test public void testSynchronizedPoolableFactoryPoolableObjectFactory() throws Exception { try { PoolUtils.synchronizedPooledFactory((PooledObjectFactory) null); fail("PoolUtils.synchronizedPoolableFactory(PoolableObjectFactory) must not allow a null factory."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); @SuppressWarnings("unchecked") final PooledObjectFactory pof = createProxy(PooledObjectFactory.class, calledMethods); final PooledObjectFactory spof = PoolUtils.synchronizedPooledFactory(pof); final List expectedMethods = invokeEveryMethod(spof); assertEquals(expectedMethods, calledMethods); // TODO: Anyone feel motivated to construct a test that verifies proper synchronization? } @Test public void testSynchronizedPoolKeyedObjectPool() throws Exception { try (final KeyedObjectPool synchronizedPool = PoolUtils .synchronizedPool((KeyedObjectPool) null)) { fail("PoolUtils.synchronizedPool(KeyedObjectPool) must not allow a null pool."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); try (@SuppressWarnings("unchecked") final KeyedObjectPool kop = createProxy(KeyedObjectPool.class, calledMethods); final KeyedObjectPool skop = PoolUtils.synchronizedPool(kop)) { final List expectedMethods = invokeEveryMethod(skop); assertEquals(expectedMethods, calledMethods); } // TODO: Anyone feel motivated to construct a test that verifies proper synchronization? } @Test public void testSynchronizedPoolObjectPool() throws Exception { try (final ObjectPool synchronizedPool = PoolUtils.synchronizedPool((ObjectPool) null)) { fail("PoolUtils.synchronizedPool(ObjectPool) must not allow a null pool."); } catch (final IllegalArgumentException iae) { // expected } final List calledMethods = new ArrayList<>(); try (@SuppressWarnings("unchecked") final ObjectPool op = createProxy(ObjectPool.class, calledMethods); final ObjectPool sop = PoolUtils.synchronizedPool(op)) { final List expectedMethods = invokeEveryMethod(sop); assertEquals(expectedMethods, calledMethods); // TODO: Anyone feel motivated to construct a test that verifies proper synchronization? } } /** * Tests the {@link PoolUtils} timer holder. */ @Test public void testTimerHolder() { final PoolUtils.TimerHolder h = new PoolUtils.TimerHolder(); assertNotNull(h); assertNotNull(PoolUtils.TimerHolder.MIN_IDLE_TIMER); } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/VisitTracker.java000066400000000000000000000052141405425132200320760ustar00rootroot00000000000000/* * 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.commons.pool2; /** * Test pooled object class. Keeps track of how many times it has been * validated, activated, passivated. * */ public class VisitTracker { private int validateCount; private int activateCount; private int passivateCount; private boolean destroyed; private int id; private K key; public VisitTracker() { reset(); } public VisitTracker(final int id) { this.id = id; reset(); } public VisitTracker(final int id, final K key) { this.id = id; this.key = key; reset(); } public void activate() { if (destroyed) { fail("attempted to activate a destroyed object"); } activateCount++; } public void destroy() { destroyed = true; } private void fail(final String message) { throw new IllegalStateException(message); } public int getActivateCount() { return activateCount; } public int getId() { return id; } public K getKey() { return key; } public int getPassivateCount() { return passivateCount; } public int getValidateCount() { return validateCount; } public boolean isDestroyed() { return destroyed; } public void passivate() { if (destroyed) { fail("attempted to passivate a destroyed object"); } passivateCount++; } public void reset() { validateCount = 0; activateCount = 0; passivateCount = 0; destroyed = false; } @Override public String toString() { return "Key: " + key + " id: " + id; } public boolean validate() { if (destroyed) { fail("attempted to validate a destroyed object"); } validateCount++; return true; } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/VisitTrackerFactory.java000066400000000000000000000052751405425132200334350ustar00rootroot00000000000000/* * 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.commons.pool2; import org.apache.commons.pool2.impl.DefaultPooledObject; /** * Factory that creates VisitTracker instances. Used to test Evictor runs. * */ public class VisitTrackerFactory implements PooledObjectFactory>, KeyedPooledObjectFactory> { private int nextId; public VisitTrackerFactory() { } @Override public void activateObject(final K key, final PooledObject> ref) throws Exception { ref.getObject().activate(); } @Override public void activateObject(final PooledObject> ref) throws Exception { ref.getObject().activate(); } @Override public void destroyObject(final K key, final PooledObject> ref) { ref.getObject().destroy(); } @Override public void destroyObject(final PooledObject> ref) { ref.getObject().destroy(); } @Override public PooledObject> makeObject() { return new DefaultPooledObject<>(new VisitTracker<>(nextId++)); } @Override public PooledObject> makeObject(final K key) { return new DefaultPooledObject<>(new VisitTracker<>(nextId++, key)); } @Override public void passivateObject(final K key, final PooledObject> ref) throws Exception { ref.getObject().passivate(); } @Override public void passivateObject(final PooledObject> ref) throws Exception { ref.getObject().passivate(); } public void resetId() { nextId = 0; } @Override public boolean validateObject(final K key, final PooledObject> ref) { return ref.getObject().validate(); } @Override public boolean validateObject(final PooledObject> ref) { return ref.getObject().validate(); } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/Waiter.java000066400000000000000000000130441405425132200307170ustar00rootroot00000000000000/* * 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.commons.pool2; import java.util.concurrent.atomic.AtomicInteger; /** *

Object created by {@link WaiterFactory}. Maintains active / valid state, * last passivated and idle times. Waits with configurable latency when * {@link #doWait()} method is called.

* *

This class is *not* threadsafe.

*/ public class Waiter { private static final AtomicInteger instanceCount = new AtomicInteger(); private boolean active; private boolean valid; private long latency; private long lastPassivatedMillis; private long lastIdleTimeMillis; private long passivationCount; private long validationCount; private final int id = instanceCount.getAndIncrement(); public Waiter(final boolean active, final boolean valid, final long latency) { this.active = active; this.valid = valid; this.latency = latency; this.lastPassivatedMillis = System.currentTimeMillis(); } /** * Wait for {@link #getLatency()} ms. */ public void doWait() { try { Thread.sleep(latency); } catch (final InterruptedException ex) { // ignore } } @Override public boolean equals(final Object obj) { if (!(obj instanceof Waiter)) { return false; } return obj.hashCode() == id; } /** *

Returns the last idle time for this instance in ms.

* *

When an instance is created, and each subsequent time it is passivated, * the {@link #getLastPassivatedMillis() lastPassivated} property is updated with the * current time. When the next activation occurs, {@code lastIdleTime} is * updated with the elapsed time since passivation.

* * @return last idle time */ public long getLastIdleTimeMillis() { return lastIdleTimeMillis; } /** *

Returns the system time of this instance's last passivation.

* *

When an instance is created, this field is initialized to the system time.

* * @return time of last passivation */ public long getLastPassivatedMillis() { return lastPassivatedMillis; } public long getLatency() { return latency; } /** * @return how many times this instance has been passivated */ public long getPassivationCount() { return passivationCount; } /** * @return how many times this instance has been validated */ public long getValidationCount() { return validationCount; } @Override public int hashCode() { return id; } /** * Whether or not the instance is active. * * @return true if the last lifecycle event for this instance was activation. */ public boolean isActive() { return active; } public boolean isValid() { validationCount++; return valid; } /** *

Sets the active state and updates {@link #getLastIdleTimeMillis() lastIdleTime} * or {@link #getLastPassivatedMillis() lastPassivated} as appropriate.

* *

If the active state is changing from inactive to active, lastIdleTime * is updated with the current time minus lastPassivated. If the state is * changing from active to inactive, lastPassivated is updated with the * current time.

* *

{@link WaiterFactory#activateObject(PooledObject)} and * {@link WaiterFactory#passivateObject(PooledObject)} invoke this method on * their actual parameter, passing {@code true} and {@code false}, * respectively.

* * @param active new active state */ public void setActive(final boolean active) { final boolean activeState = this.active; if (activeState == active) { return; } this.active = active; final long currentTimeMillis = System.currentTimeMillis(); if (active) { // activating lastIdleTimeMillis = currentTimeMillis - lastPassivatedMillis; } else { // passivating lastPassivatedMillis = currentTimeMillis; passivationCount++; } } public void setLatency(final long latency) { this.latency = latency; } public void setValid(final boolean valid) { this.valid = valid; } @Override public String toString() { final StringBuilder buff = new StringBuilder(); buff.append("ID = " + id + '\n'); buff.append("valid = " + valid + '\n'); buff.append("active = " + active + '\n'); buff.append("lastPassivated = " + lastPassivatedMillis + '\n'); buff.append("lastIdleTimeMs = " + lastIdleTimeMillis + '\n'); buff.append("latency = " + latency + '\n'); return buff.toString(); } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/WaiterFactory.java000066400000000000000000000173141405425132200322530ustar00rootroot00000000000000/* * 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.commons.pool2; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.apache.commons.pool2.impl.DefaultPooledObject; /** * Object factory with configurable latencies for object lifecycle methods. * This factory will also track and enforce maxActive, maxActivePerKey contracts. * If the factory's maxActive / maxActivePerKey are set to match those of the * pool, makeObject will throw IllegalStateException if the number of makes - destroys * (per key) exceeds the configured max. * */ public class WaiterFactory implements PooledObjectFactory, KeyedPooledObjectFactory { /** Latency of activateObject */ private final long activateLatency; /** Latency of destroyObject */ private final long destroyLatency; /** Latency of makeObject */ private final long makeLatency; /** Latency of passivateObject */ private final long passivateLatency; /** Latency of validateObject */ private final long validateLatency; /** Latency of doWait for Waiter instances created by this factory */ private final long waiterLatency; /** Probability that passivation will invalidate Waiter instances */ private final double passivateInvalidationProbability; /** Count of (makes - destroys) since last reset */ private long activeCount; /** Count of (makes - destroys) per key since last reset */ private final Map activeCounts = new HashMap<>(); /** Maximum of (makes - destroys) - if exceeded IllegalStateException */ private final long maxActive; // GKOP 1.x calls this maxTotal /** Maximum of (makes - destroys) per key */ private final long maxActivePerKey; // GKOP 1.x calls this maxActive public WaiterFactory(final long activateLatency, final long destroyLatency, final long makeLatency, final long passivateLatency, final long validateLatency, final long waiterLatency) { this(activateLatency, destroyLatency, makeLatency, passivateLatency, validateLatency, waiterLatency, Long.MAX_VALUE, Long.MAX_VALUE, 0); } public WaiterFactory(final long activateLatency, final long destroyLatency, final long makeLatency, final long passivateLatency, final long validateLatency, final long waiterLatency,final long maxActive) { this(activateLatency, destroyLatency, makeLatency, passivateLatency, validateLatency, waiterLatency, maxActive, Long.MAX_VALUE, 0); } public WaiterFactory(final long activateLatency, final long destroyLatency, final long makeLatency, final long passivateLatency, final long validateLatency, final long waiterLatency,final long maxActive, final long maxActivePerKey, final double passivateInvalidationProbability) { this.activateLatency = activateLatency; this.destroyLatency = destroyLatency; this.makeLatency = makeLatency; this.passivateLatency = passivateLatency; this.validateLatency = validateLatency; this.waiterLatency = waiterLatency; this.maxActive = maxActive; this.maxActivePerKey = maxActivePerKey; this.passivateInvalidationProbability = passivateInvalidationProbability; } @Override public void activateObject(final K key, final PooledObject obj) throws Exception { activateObject(obj); } @Override public void activateObject(final PooledObject obj) throws Exception { doWait(activateLatency); obj.getObject().setActive(true); } @Override public void destroyObject(final K key,final PooledObject obj) throws Exception { destroyObject(obj); synchronized (this) { final Integer count = activeCounts.get(key); activeCounts.put(key, Integer.valueOf(count.intValue() - 1)); } } @Override public void destroyObject(final PooledObject obj) throws Exception { doWait(destroyLatency); obj.getObject().setValid(false); obj.getObject().setActive(false); // Decrement *after* destroy synchronized (this) { activeCount--; } } protected void doWait(final long latency) { if (latency == 0) { return; } try { Thread.sleep(latency); } catch (final InterruptedException ex) { // ignore } } /** * @return the maxActive */ public synchronized long getMaxActive() { return maxActive; } @Override public PooledObject makeObject() throws Exception { // Increment and test *before* make synchronized (this) { if (activeCount >= maxActive) { throw new IllegalStateException("Too many active instances: " + activeCount + " in circulation with maxActive = " + maxActive); } activeCount++; } doWait(makeLatency); return new DefaultPooledObject<>(new Waiter(false, true, waiterLatency)); } @Override public PooledObject makeObject(final K key) throws Exception { synchronized (this) { Integer count = activeCounts.get(key); if (count == null) { count = Integer.valueOf(1); activeCounts.put(key, count); } else { if (count.intValue() >= maxActivePerKey) { throw new IllegalStateException("Too many active " + "instances for key = " + key + ": " + count.intValue() + " in circulation " + "with maxActivePerKey = " + maxActivePerKey); } activeCounts.put(key, Integer.valueOf(count.intValue() + 1)); } } return makeObject(); } // KeyedPoolableObjectFactory methods @Override public void passivateObject(final K key, final PooledObject obj) throws Exception { passivateObject(obj); } @Override public void passivateObject(final PooledObject obj) throws Exception { obj.getObject().setActive(false); doWait(passivateLatency); if (Math.random() < passivateInvalidationProbability) { obj.getObject().setValid(false); } } public synchronized void reset() { activeCount = 0; if (activeCounts.isEmpty()) { return; } final Iterator it = activeCounts.keySet().iterator(); while (it.hasNext()) { final K key = it.next(); activeCounts.put(key, Integer.valueOf(0)); } } @Override public boolean validateObject(final K key, final PooledObject obj) { return validateObject(obj); } @Override public boolean validateObject(final PooledObject obj) { doWait(validateLatency); return obj.getObject().isValid(); } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/000077500000000000000000000000001405425132200275605ustar00rootroot00000000000000AtomicIntegerFactory.java000066400000000000000000000075311405425132200344340ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; /** * Factory that sources PooledObjects that wrap AtomicIntegers. * {@link #create()} creates an AtomicInteger with value 0, activate increments * the value of the wrapped AtomicInteger and passivate decrements it. Latency * of factory methods is configurable. * */ public class AtomicIntegerFactory extends BasePooledObjectFactory { private long activateLatency; private long passivateLatency; private long createLatency; private long destroyLatency; private long validateLatency; @Override public void activateObject(final PooledObject p) { p.getObject().incrementAndGet(); try { Thread.sleep(activateLatency); } catch (final InterruptedException ex) { // ignore } } @Override public AtomicInteger create() { try { Thread.sleep(createLatency); } catch (final InterruptedException ex) { // ignore } return new AtomicInteger(0); } @Override public void destroyObject(final PooledObject p) { try { Thread.sleep(destroyLatency); } catch (final InterruptedException ex) { // ignore } } @Override public void passivateObject(final PooledObject p) { p.getObject().decrementAndGet(); try { Thread.sleep(passivateLatency); } catch (final InterruptedException ex) { // ignore } } /** * @param activateLatency the activateLatency to set */ public void setActivateLatency(final long activateLatency) { this.activateLatency = activateLatency; } /** * @param createLatency the createLatency to set */ public void setCreateLatency(final long createLatency) { this.createLatency = createLatency; } /** * @param destroyLatency the destroyLatency to set */ public void setDestroyLatency(final long destroyLatency) { this.destroyLatency = destroyLatency; } /** * @param passivateLatency the passivateLatency to set */ public void setPassivateLatency(final long passivateLatency) { this.passivateLatency = passivateLatency; } /** * @param validateLatency the validateLatency to set */ public void setValidateLatency(final long validateLatency) { this.validateLatency = validateLatency; } @Override public boolean validateObject(final PooledObject instance) { try { Thread.sleep(validateLatency); } catch (final InterruptedException ex) { // ignore } return instance.getObject().intValue() == 1; } @Override public PooledObject wrap(final AtomicInteger integer) { return new DefaultPooledObject<>(integer); } }commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/CallStackTest.java000066400000000000000000000043101405425132200331220ustar00rootroot00000000000000/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.PrintWriter; import java.io.StringWriter; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; public class CallStackTest { public static Stream data() { return Stream.of( Arguments.arguments( new ThrowableCallStack("Test", false)), Arguments.arguments( new SecurityManagerCallStack("Test", false)) ); } private final StringWriter writer = new StringWriter(); @ParameterizedTest @MethodSource("data") public void testPrintClearedStackTraceIsNoOp(final CallStack stack) throws Exception { stack.fillInStackTrace(); stack.clear(); stack.printStackTrace(new PrintWriter(writer)); final String stackTrace = writer.toString(); assertEquals("", stackTrace); } @ParameterizedTest @MethodSource("data") public void testPrintFilledStackTrace(final CallStack stack) throws Exception { stack.fillInStackTrace(); stack.printStackTrace(new PrintWriter(writer)); final String stackTrace = writer.toString(); assertTrue(stackTrace.contains(getClass().getName())); } }NoOpCallStackTest.java000066400000000000000000000025131405425132200336420ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.PrintWriter; import java.io.StringWriter; import org.junit.jupiter.api.Test; public class NoOpCallStackTest { @Test public void printStackTraceIsNoOp() throws Exception { final CallStack stack = NoOpCallStack.INSTANCE; stack.fillInStackTrace(); final StringWriter writer = new StringWriter(); stack.printStackTrace(new PrintWriter(writer)); assertEquals("", writer.toString()); } }TestAbandonedKeyedObjectPool.java000066400000000000000000000375661405425132200360430ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.lang.management.ManagementFactory; import java.time.Duration; import java.util.ArrayList; import java.util.Objects; import java.util.Set; import javax.management.MBeanServer; import javax.management.ObjectName; import org.apache.commons.pool2.DestroyMode; import org.apache.commons.pool2.KeyedPooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * TestCase for AbandonedObjectPool */ public class TestAbandonedKeyedObjectPool { class ConcurrentBorrower extends Thread { private final ArrayList _borrowed; public ConcurrentBorrower(final ArrayList borrowed) { _borrowed = borrowed; } @Override public void run() { try { _borrowed.add(pool.borrowObject(0)); } catch (final Exception e) { // expected in most cases } } } class ConcurrentReturner extends Thread { private final PooledTestObject returned; public ConcurrentReturner(final PooledTestObject obj) { returned = obj; } @Override public void run() { try { sleep(20); pool.returnObject(0,returned); } catch (final Exception e) { // ignore } } } private static class SimpleFactory implements KeyedPooledObjectFactory { private final long destroyLatency; private final long validateLatency; public SimpleFactory() { destroyLatency = 0; validateLatency = 0; } public SimpleFactory(final long destroyLatency, final long validateLatency) { this.destroyLatency = destroyLatency; this.validateLatency = validateLatency; } @Override public void activateObject(final Integer key, final PooledObject obj) { obj.getObject().setActive(true); } @Override public void destroyObject(final Integer key, final PooledObject obj) throws Exception { destroyObject(key, obj, DestroyMode.NORMAL); } @Override public void destroyObject(final Integer key, final PooledObject obj, final DestroyMode mode) throws Exception { obj.getObject().setActive(false); // while destroying instances, yield control to other threads // helps simulate threading errors Thread.yield(); if (destroyLatency != 0) { Thread.sleep(destroyLatency); } obj.getObject().destroy(mode); } @Override public PooledObject makeObject(final Integer key) { return new DefaultPooledObject<>(new PooledTestObject()); } @Override public void passivateObject(final Integer key, final PooledObject obj) { obj.getObject().setActive(false); } @Override public boolean validateObject(final Integer key, final PooledObject obj) { try { Thread.sleep(validateLatency); } catch (final Exception ex) { // ignore } return true; } } private GenericKeyedObjectPool pool = null; private AbandonedConfig abandonedConfig = null; @SuppressWarnings("deprecation") @BeforeEach public void setUp() throws Exception { abandonedConfig = new AbandonedConfig(); // -- Uncomment the following line to enable logging -- // abandonedConfig.setLogAbandoned(true); abandonedConfig.setRemoveAbandonedOnBorrow(true); abandonedConfig.setRemoveAbandonedTimeout(1); assertEquals(TestConstants.ONE_SECOND, abandonedConfig.getRemoveAbandonedTimeoutDuration()); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); assertEquals(1, abandonedConfig.getRemoveAbandonedTimeout()); pool = new GenericKeyedObjectPool<>( new SimpleFactory(), new GenericKeyedObjectPoolConfig<>(), abandonedConfig); } @AfterEach public void tearDown() throws Exception { final ObjectName jmxName = pool.getJmxName(); final String poolName = Objects.toString(jmxName, null); pool.clear(); pool.close(); pool = null; final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); final Set result = mbs.queryNames(new ObjectName( "org.apache.commoms.pool2:type=GenericKeyedObjectPool,*"), null); // There should be no registered pools at this point final int registeredPoolCount = result.size(); final StringBuilder msg = new StringBuilder("Current pool is: "); msg.append(poolName); msg.append(" Still open pools are: "); for (final ObjectName name : result) { // Clean these up ready for the next test msg.append(name.toString()); msg.append(" created via\n"); msg.append(mbs.getAttribute(name, "CreationStackTrace")); msg.append('\n'); mbs.unregisterMBean(name); } assertEquals( 0, registeredPoolCount,msg.toString()); } /** * Verify that an object that gets flagged as abandoned and is subsequently * invalidated is only destroyed (and pool counter decremented) once. * * @throws Exception May occur in some failure modes */ @Test public void testAbandonedInvalidate() throws Exception { abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnMaintenance(true); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); pool.close(); // Unregister pool created by setup pool = new GenericKeyedObjectPool<>( // destroys take 200 ms new SimpleFactory(200, 0), new GenericKeyedObjectPoolConfig<>(), abandonedConfig); final int n = 10; pool.setMaxTotal(n); pool.setBlockWhenExhausted(false); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); PooledTestObject obj = null; for (int i = 0; i < 5; i++) { obj = pool.borrowObject(0); } Thread.sleep(1000); // abandon checked out instances and let evictor start pool.invalidateObject(0, obj); // Should not trigger another destroy / decrement Thread.sleep(2000); // give evictor time to finish destroys assertEquals(0, pool.getNumActive()); assertEquals(5, pool.getDestroyedCount()); } /** * Verify that an object that gets flagged as abandoned and is subsequently returned * is destroyed instead of being returned to the pool (and possibly later destroyed * inappropriately). * * @throws Exception May occur in some failure modes */ @Test public void testAbandonedReturn() throws Exception { abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnBorrow(true); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); pool.close(); // Unregister pool created by setup pool = new GenericKeyedObjectPool<>( new SimpleFactory(200, 0), new GenericKeyedObjectPoolConfig<>(), abandonedConfig); final int n = 10; pool.setMaxTotal(n); pool.setBlockWhenExhausted(false); PooledTestObject obj = null; for (int i = 0; i < n - 2; i++) { obj = pool.borrowObject(0); } Objects.requireNonNull(obj, "Unable to borrow object from pool"); final int deadMansHash = obj.hashCode(); final ConcurrentReturner returner = new ConcurrentReturner(obj); Thread.sleep(2000); // abandon checked out instances // Now start a race - returner waits until borrowObject has kicked // off removeAbandoned and then returns an instance that borrowObject // will deem abandoned. Make sure it is not returned to the borrower. returner.start(); // short delay, then return instance assertTrue(pool.borrowObject(0).hashCode() != deadMansHash); assertEquals(0, pool.getNumIdle()); assertEquals(1, pool.getNumActive()); } /** * Tests fix for Bug 28579, a bug in AbandonedObjectPool that causes numActive to go negative * in GenericKeyedObjectPool * * @throws Exception May occur in some failure modes */ @Test public void testConcurrentInvalidation() throws Exception { final int POOL_SIZE = 30; pool.setMaxTotalPerKey(POOL_SIZE); pool.setMaxIdlePerKey(POOL_SIZE); pool.setBlockWhenExhausted(false); // Exhaust the connection pool final ArrayList vec = new ArrayList<>(); for (int i = 0; i < POOL_SIZE; i++) { vec.add(pool.borrowObject(0)); } // Abandon all borrowed objects for (final PooledTestObject element : vec) { element.setAbandoned(true); } // Try launching a bunch of borrows concurrently. Abandoned sweep will be triggered for each. final int CONCURRENT_BORROWS = 5; final Thread[] threads = new Thread[CONCURRENT_BORROWS]; for (int i = 0; i < CONCURRENT_BORROWS; i++) { threads[i] = new ConcurrentBorrower(vec); threads[i].start(); } // Wait for all the threads to finish for (int i = 0; i < CONCURRENT_BORROWS; i++) { threads[i].join(); } // Return all objects that have not been destroyed for (final PooledTestObject pto : vec) { if (pto.isActive()) { pool.returnObject(0, pto); } } // Now, the number of active instances should be 0 assertEquals(0, pool.getNumActive(), "numActive should have been 0, was " + pool.getNumActive()); } public void testDestroyModeAbandoned() throws Exception { abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnMaintenance(true); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); pool.close(); // Unregister pool created by setup pool = new GenericKeyedObjectPool<>( // validate takes 1 second new SimpleFactory(0, 0), new GenericKeyedObjectPoolConfig<>(), abandonedConfig); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(50)); // Borrow an object, wait long enough for it to be abandoned final PooledTestObject obj = pool.borrowObject(0); Thread.sleep(100); assertTrue(obj.isDetached()); } public void testDestroyModeNormal() throws Exception { abandonedConfig = new AbandonedConfig(); pool.close(); // Unregister pool created by setup pool = new GenericKeyedObjectPool<>(new SimpleFactory(0, 0)); pool.setMaxIdlePerKey(0); final PooledTestObject obj = pool.borrowObject(0); pool.returnObject(0, obj); assertTrue(obj.isDestroyed()); assertFalse(obj.isDetached()); } /** * Verify that an object that the evictor identifies as abandoned while it * is in process of being returned to the pool is not destroyed. * * @throws Exception May occur in some failure modes */ @Test public void testRemoveAbandonedWhileReturning() throws Exception { abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnMaintenance(true); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); pool.close(); // Unregister pool created by setup pool = new GenericKeyedObjectPool<>( // validate takes 1 second new SimpleFactory(0, 1000), new GenericKeyedObjectPoolConfig<>(), abandonedConfig); final int n = 10; pool.setMaxTotal(n); pool.setBlockWhenExhausted(false); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); pool.setTestOnReturn(true); // Borrow an object, wait long enough for it to be abandoned // then arrange for evictor to run while it is being returned // validation takes a second, evictor runs every 500 ms final PooledTestObject obj = pool.borrowObject(0); Thread.sleep(50); // abandon obj pool.returnObject(0,obj); // evictor will run during validation final PooledTestObject obj2 = pool.borrowObject(0); assertEquals(obj, obj2); // should get original back assertFalse(obj2.isDestroyed()); // and not destroyed } /** * JIRA: POOL-300 */ @Test public void testStackTrace() throws Exception { abandonedConfig.setRemoveAbandonedOnMaintenance(true); abandonedConfig.setLogAbandoned(true); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final BufferedOutputStream bos = new BufferedOutputStream(baos); final PrintWriter pw = new PrintWriter(bos); abandonedConfig.setLogWriter(pw); pool.setAbandonedConfig(abandonedConfig); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); final PooledTestObject o1 = pool.borrowObject(0); Thread.sleep(2000); assertTrue(o1.isDestroyed()); bos.flush(); assertTrue(baos.toString().indexOf("Pooled object") >= 0); } /** * Test case for https://issues.apache.org/jira/browse/DBCP-260. * Borrow and abandon all the available objects then attempt to borrow one * further object which should block until the abandoned objects are * removed. We don't want the test to block indefinitely when it fails so * use maxWait be check we don't actually have to wait that long. * * @throws Exception May occur in some failure modes */ @Test public void testWhenExhaustedBlock() throws Exception { abandonedConfig.setRemoveAbandonedOnMaintenance(true); pool.setAbandonedConfig(abandonedConfig); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); pool.setMaxTotal(1); @SuppressWarnings("unused") // This is going to be abandoned final PooledTestObject o1 = pool.borrowObject(0); final long startMillis = System.currentTimeMillis(); final PooledTestObject o2 = pool.borrowObject(0, 5000); final long endMillis = System.currentTimeMillis(); pool.returnObject(0, o2); assertTrue(endMillis - startMillis < 5000); } } TestAbandonedObjectPool.java000066400000000000000000000424051405425132200350450ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.lang.management.ManagementFactory; import java.time.Duration; import java.util.ArrayList; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import javax.management.MBeanServer; import javax.management.ObjectName; import org.apache.commons.pool2.DestroyMode; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.TrackedUse; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class PooledTestObject implements TrackedUse { private static final AtomicInteger hash = new AtomicInteger(); private boolean active = false; private boolean destroyed = false; private int _hash = 0; private boolean _abandoned = false; private boolean detached = false; // destroy-abandoned "detaches" public PooledTestObject() { _hash = hash.incrementAndGet(); } public void destroy(final DestroyMode mode) { destroyed = true; if (mode.equals(DestroyMode.ABANDONED)) { detached = true; } } @Override public boolean equals(final Object obj) { if (!(obj instanceof PooledTestObject)) { return false; } return obj.hashCode() == hashCode(); } @Override public long getLastUsed() { if (_abandoned) { // Abandoned object sweep will occur no matter what the value of removeAbandonedTimeout, // because this indicates that this object was last used decades ago return 1; } // Abandoned object sweep won't clean up this object return 0; } @Override public int hashCode() { return _hash; } public synchronized boolean isActive() { return active; } public boolean isDestroyed() { return destroyed; } public boolean isDetached() { return detached; } public void setAbandoned(final boolean b) { _abandoned = b; } public synchronized void setActive(final boolean b) { active = b; } } /** * TestCase for AbandonedObjectPool */ public class TestAbandonedObjectPool { class ConcurrentBorrower extends Thread { private final ArrayList _borrowed; public ConcurrentBorrower(final ArrayList borrowed) { _borrowed = borrowed; } @Override public void run() { try { _borrowed.add(pool.borrowObject()); } catch (final Exception e) { // expected in most cases } } } class ConcurrentReturner extends Thread { private final PooledTestObject returned; public ConcurrentReturner(final PooledTestObject obj) { returned = obj; } @Override public void run() { try { sleep(20); pool.returnObject(returned); } catch (final Exception e) { // ignore } } } private static class SimpleFactory implements PooledObjectFactory { private final long destroyLatency; private final long validateLatency; public SimpleFactory() { destroyLatency = 0; validateLatency = 0; } public SimpleFactory(final long destroyLatency, final long validateLatency) { this.destroyLatency = destroyLatency; this.validateLatency = validateLatency; } @Override public void activateObject(final PooledObject obj) { obj.getObject().setActive(true); } @Override public void destroyObject(final PooledObject obj) throws Exception { destroyObject(obj, DestroyMode.NORMAL); } @Override public void destroyObject(final PooledObject obj, final DestroyMode mode) throws Exception { obj.getObject().setActive(false); // while destroying instances, yield control to other threads // helps simulate threading errors Thread.yield(); if (destroyLatency != 0) { Thread.sleep(destroyLatency); } obj.getObject().destroy(mode); } @Override public PooledObject makeObject() { return new DefaultPooledObject<>(new PooledTestObject()); } @Override public void passivateObject(final PooledObject obj) { obj.getObject().setActive(false); } @Override public boolean validateObject(final PooledObject obj) { try { Thread.sleep(validateLatency); } catch (final Exception ex) { // ignore } return true; } } private GenericObjectPool pool = null; private AbandonedConfig abandonedConfig = null; @SuppressWarnings("deprecation") @BeforeEach public void setUp() throws Exception { abandonedConfig = new AbandonedConfig(); // -- Uncomment the following line to enable logging -- // abandonedConfig.setLogAbandoned(true); abandonedConfig.setRemoveAbandonedOnBorrow(true); abandonedConfig.setRemoveAbandonedTimeout(1); assertEquals(TestConstants.ONE_SECOND, abandonedConfig.getRemoveAbandonedTimeoutDuration()); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); assertEquals(1, abandonedConfig.getRemoveAbandonedTimeout()); pool = new GenericObjectPool<>( new SimpleFactory(), new GenericObjectPoolConfig<>(), abandonedConfig); } @AfterEach public void tearDown() throws Exception { final ObjectName jmxName = pool.getJmxName(); final String poolName = Objects.toString(jmxName, null); pool.clear(); pool.close(); pool = null; final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); final Set result = mbs.queryNames(new ObjectName( "org.apache.commoms.pool2:type=GenericObjectPool,*"), null); // There should be no registered pools at this point final int registeredPoolCount = result.size(); final StringBuilder msg = new StringBuilder("Current pool is: "); msg.append(poolName); msg.append(" Still open pools are: "); for (final ObjectName name : result) { // Clean these up ready for the next test msg.append(name.toString()); msg.append(" created via\n"); msg.append(mbs.getAttribute(name, "CreationStackTrace")); msg.append('\n'); mbs.unregisterMBean(name); } assertEquals( 0, registeredPoolCount,msg.toString()); } /** * Verify that an object that gets flagged as abandoned and is subsequently * invalidated is only destroyed (and pool counter decremented) once. * * @throws Exception May occur in some failure modes */ @Test public void testAbandonedInvalidate() throws Exception { abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnMaintenance(true); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); pool.close(); // Unregister pool created by setup pool = new GenericObjectPool<>( // destroys take 200 ms new SimpleFactory(200, 0), new GenericObjectPoolConfig<>(), abandonedConfig); final int n = 10; pool.setMaxTotal(n); pool.setBlockWhenExhausted(false); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); PooledTestObject obj = null; for (int i = 0; i < 5; i++) { obj = pool.borrowObject(); } Thread.sleep(1000); // abandon checked out instances and let evictor start pool.invalidateObject(obj); // Should not trigger another destroy / decrement Thread.sleep(2000); // give evictor time to finish destroys assertEquals(0, pool.getNumActive()); assertEquals(5, pool.getDestroyedCount()); } /** * Verify that an object that gets flagged as abandoned and is subsequently returned * is destroyed instead of being returned to the pool (and possibly later destroyed * inappropriately). * * @throws Exception May occur in some failure modes */ @Test public void testAbandonedReturn() throws Exception { abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnBorrow(true); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); pool.close(); // Unregister pool created by setup pool = new GenericObjectPool<>( new SimpleFactory(200, 0), new GenericObjectPoolConfig<>(), abandonedConfig); final int n = 10; pool.setMaxTotal(n); pool.setBlockWhenExhausted(false); PooledTestObject obj = null; for (int i = 0; i < n - 2; i++) { obj = pool.borrowObject(); } Objects.requireNonNull(obj, "Unable to borrow object from pool"); final int deadMansHash = obj.hashCode(); final ConcurrentReturner returner = new ConcurrentReturner(obj); Thread.sleep(2000); // abandon checked out instances // Now start a race - returner waits until borrowObject has kicked // off removeAbandoned and then returns an instance that borrowObject // will deem abandoned. Make sure it is not returned to the borrower. returner.start(); // short delay, then return instance assertTrue(pool.borrowObject().hashCode() != deadMansHash); assertEquals(0, pool.getNumIdle()); assertEquals(1, pool.getNumActive()); } /** * Tests fix for Bug 28579, a bug in AbandonedObjectPool that causes numActive to go negative * in GenericObjectPool * * @throws Exception May occur in some failure modes */ @Test public void testConcurrentInvalidation() throws Exception { final int POOL_SIZE = 30; pool.setMaxTotal(POOL_SIZE); pool.setMaxIdle(POOL_SIZE); pool.setBlockWhenExhausted(false); // Exhaust the connection pool final ArrayList vec = new ArrayList<>(); for (int i = 0; i < POOL_SIZE; i++) { vec.add(pool.borrowObject()); } // Abandon all borrowed objects for (final PooledTestObject element : vec) { element.setAbandoned(true); } // Try launching a bunch of borrows concurrently. Abandoned sweep will be triggered for each. final int CONCURRENT_BORROWS = 5; final Thread[] threads = new Thread[CONCURRENT_BORROWS]; for (int i = 0; i < CONCURRENT_BORROWS; i++) { threads[i] = new ConcurrentBorrower(vec); threads[i].start(); } // Wait for all the threads to finish for (int i = 0; i < CONCURRENT_BORROWS; i++) { threads[i].join(); } // Return all objects that have not been destroyed for (final PooledTestObject pto : vec) { if (pto.isActive()) { pool.returnObject(pto); } } // Now, the number of active instances should be 0 assertEquals(0, pool.getNumActive(), "numActive should have been 0, was " + pool.getNumActive()); } public void testDestroyModeAbandoned() throws Exception { abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnMaintenance(true); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); pool.close(); // Unregister pool created by setup pool = new GenericObjectPool<>( // validate takes 1 second new SimpleFactory(0, 0), new GenericObjectPoolConfig<>(), abandonedConfig); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(50)); // Borrow an object, wait long enough for it to be abandoned final PooledTestObject obj = pool.borrowObject(); Thread.sleep(100); assertTrue(obj.isDetached()); } public void testDestroyModeNormal() throws Exception { abandonedConfig = new AbandonedConfig(); pool.close(); // Unregister pool created by setup pool = new GenericObjectPool<>(new SimpleFactory(0, 0)); pool.setMaxIdle(0); final PooledTestObject obj = pool.borrowObject(); pool.returnObject(obj); assertTrue(obj.isDestroyed()); assertFalse(obj.isDetached()); } /** * Verify that an object that the evictor identifies as abandoned while it * is in process of being returned to the pool is not destroyed. * * @throws Exception May occur in some failure modes */ @Test public void testRemoveAbandonedWhileReturning() throws Exception { abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnMaintenance(true); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); pool.close(); // Unregister pool created by setup pool = new GenericObjectPool<>( // validate takes 1 second new SimpleFactory(0, 1000), new GenericObjectPoolConfig<>(), abandonedConfig); final int n = 10; pool.setMaxTotal(n); pool.setBlockWhenExhausted(false); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); pool.setTestOnReturn(true); // Borrow an object, wait long enough for it to be abandoned // then arrange for evictor to run while it is being returned // validation takes a second, evictor runs every 500 ms final PooledTestObject obj = pool.borrowObject(); Thread.sleep(50); // abandon obj pool.returnObject(obj); // evictor will run during validation final PooledTestObject obj2 = pool.borrowObject(); assertEquals(obj, obj2); // should get original back assertFalse(obj2.isDestroyed()); // and not destroyed } /** * JIRA: POOL-300 */ @Test public void testStackTrace() throws Exception { abandonedConfig.setRemoveAbandonedOnMaintenance(true); abandonedConfig.setLogAbandoned(true); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final BufferedOutputStream bos = new BufferedOutputStream(baos); final PrintWriter pw = new PrintWriter(bos); abandonedConfig.setLogWriter(pw); pool.setAbandonedConfig(abandonedConfig); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); final PooledTestObject o1 = pool.borrowObject(); Thread.sleep(2000); assertTrue(o1.isDestroyed()); bos.flush(); assertTrue(baos.toString().indexOf("Pooled object") >= 0); } /** * Test case for https://issues.apache.org/jira/browse/DBCP-260. * Borrow and abandon all the available objects then attempt to borrow one * further object which should block until the abandoned objects are * removed. We don't want the test to block indefinitely when it fails so * use maxWait be check we don't actually have to wait that long. * * @throws Exception May occur in some failure modes */ @Test public void testWhenExhaustedBlock() throws Exception { abandonedConfig.setRemoveAbandonedOnMaintenance(true); pool.setAbandonedConfig(abandonedConfig); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); pool.setMaxTotal(1); @SuppressWarnings("unused") // This is going to be abandoned final PooledTestObject o1 = pool.borrowObject(); final long startMillis = System.currentTimeMillis(); final PooledTestObject o2 = pool.borrowObject(5000); final long endMillis = System.currentTimeMillis(); pool.returnObject(o2); assertTrue(endMillis - startMillis < 5000); } } TestBaseGenericObjectPool.java000066400000000000000000000103041405425132200353320ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import java.time.Duration; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.pool2.impl.TestGenericObjectPool.SimpleFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** */ public class TestBaseGenericObjectPool { BaseGenericObjectPool pool; SimpleFactory factory; @BeforeEach public void setUp() throws Exception { factory = new SimpleFactory(); pool = new GenericObjectPool<>(factory); } @AfterEach public void tearDown() throws Exception { pool.close(); pool = null; factory = null; } @Test public void testActiveTimeStatistics() { for (int i = 0; i < 99; i++) { // must be < MEAN_TIMING_STATS_CACHE_SIZE pool.updateStatsReturn(Duration.ofMillis(i)); } assertEquals(49, pool.getMeanActiveTimeMillis(), Double.MIN_VALUE); } @Test public void testBorrowWaitStatistics() { final DefaultPooledObject p = (DefaultPooledObject) factory.makeObject(); pool.updateStatsBorrow(p, Duration.ofMillis(10)); pool.updateStatsBorrow(p, Duration.ofMillis(20)); pool.updateStatsBorrow(p, Duration.ofMillis(20)); pool.updateStatsBorrow(p, Duration.ofMillis(30)); assertEquals(20, pool.getMeanBorrowWaitTimeMillis(), Double.MIN_VALUE); assertEquals(30, pool.getMaxBorrowWaitTimeMillis(), 0); } public void testBorrowWaitStatisticsMax() { final DefaultPooledObject p = (DefaultPooledObject) factory.makeObject(); assertEquals(0, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE); pool.updateStatsBorrow(p, Duration.ZERO); assertEquals(0, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE); pool.updateStatsBorrow(p, Duration.ofMillis(20)); assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE); pool.updateStatsBorrow(p, Duration.ofMillis(20)); assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE); pool.updateStatsBorrow(p, Duration.ofMillis(10)); assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE); } @Test public void testEvictionTimerMultiplePools() throws InterruptedException { final AtomicIntegerFactory factory = new AtomicIntegerFactory(); factory.setValidateLatency(50); try (final GenericObjectPool evictingPool = new GenericObjectPool<>(factory)) { evictingPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); evictingPool.setNumTestsPerEvictionRun(5); evictingPool.setTestWhileIdle(true); evictingPool.setMinEvictableIdleTime(Duration.ofMillis(50)); for (int i = 0; i < 10; i++) { try { evictingPool.addObject(); } catch (final Exception e) { e.printStackTrace(); } } for (int i = 0; i < 1000; i++) { try (final GenericObjectPool nonEvictingPool = new GenericObjectPool<>(factory)) { // empty } } Thread.sleep(1000); assertEquals(0, evictingPool.getNumIdle()); } } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/TestConstants.java000066400000000000000000000021341405425132200332370ustar00rootroot00000000000000/* * 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.commons.pool2.impl; import java.time.Duration; public class TestConstants { public static final Duration ONE_SECOND = Duration.ofSeconds(1); public static final Duration ONE_MINUTE = Duration.ofMinutes(1); public static final Duration ONE_MILLISECOND = Duration.ofMillis(1); } TestDefaultPooledObject.java000066400000000000000000000060331405425132200350640ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertFalse; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Test; public class TestDefaultPooledObject { /** * JIRA: POOL-279 * @throws Exception May occur in some failure modes */ @Test public void testGetIdleTimeMillis() throws Exception { final DefaultPooledObject dpo = new DefaultPooledObject<>(new Object()); final AtomicBoolean negativeIdleTimeReturned = new AtomicBoolean(false); final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 3); final Runnable allocateAndDeallocateTask = () -> { for (int i1 = 0; i1 < 10000; i1++) { if (dpo.getIdleTime().isNegative()) { negativeIdleTimeReturned.set(true); break; } } dpo.allocate(); for (int i2 = 0; i2 < 10000; i2++) { if (dpo.getIdleTime().isNegative()) { negativeIdleTimeReturned.set(true); break; } } dpo.deallocate(); }; final Runnable getIdleTimeTask = () -> { for (int i = 0; i < 10000; i++) { if (dpo.getIdleTime().isNegative()) { negativeIdleTimeReturned.set(true); break; } } }; final double probabilityOfAllocationTask = 0.7; final List> futures = new ArrayList<>(); for (int i = 1; i <= 10000; i++) { final Runnable randomTask = Math.random() < probabilityOfAllocationTask ? allocateAndDeallocateTask : getIdleTimeTask; futures.add(executor.submit(randomTask)); } for (final Future future : futures) { future.get(); } assertFalse(negativeIdleTimeReturned.get(), "DefaultPooledObject.getIdleTimeMillis() returned a negative value"); } }TestDefaultPooledObjectInfo.java000066400000000000000000000115071405425132200357020ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.text.SimpleDateFormat; import java.util.Set; import org.apache.commons.pool2.impl.TestGenericObjectPool.SimpleFactory; import org.junit.jupiter.api.Test; public class TestDefaultPooledObjectInfo { @Test public void testGetLastBorrowTrace() throws Exception { final AbandonedConfig abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnBorrow(true); abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND); abandonedConfig.setLogAbandoned(true); try (final GenericObjectPool pool = new GenericObjectPool<>(new SimpleFactory(), new GenericObjectPoolConfig<>(), abandonedConfig)) { pool.borrowObject(); // pool.returnObject(s1); // Object not returned, causes abandoned object created exception final Set strings = pool.listAllObjects(); final DefaultPooledObjectInfo s1Info = strings.iterator().next(); final String lastBorrowTrace = s1Info.getLastBorrowTrace(); assertTrue(lastBorrowTrace.startsWith("Pooled object created")); } } @Test public void testGetPooledObjectToString() throws Exception { try (final GenericObjectPool pool = new GenericObjectPool<>(new SimpleFactory())) { final String s1 = pool.borrowObject(); final Set strings = pool.listAllObjects(); assertEquals(1, strings.size()); final DefaultPooledObjectInfo s1Info = strings.iterator().next(); assertEquals(s1, s1Info.getPooledObjectToString()); } } @Test public void testGetPooledObjectType() throws Exception { try (final GenericObjectPool pool = new GenericObjectPool<>(new SimpleFactory())) { pool.borrowObject(); final Set strings = pool.listAllObjects(); assertEquals(1, strings.size()); final DefaultPooledObjectInfo s1Info = strings.iterator().next(); assertEquals(String.class.getName(), s1Info.getPooledObjectType()); } } @Test public void testTiming() throws Exception { try (final GenericObjectPool pool = new GenericObjectPool<>(new SimpleFactory())) { final long t1Millis = System.currentTimeMillis(); Thread.sleep(50); final String s1 = pool.borrowObject(); Thread.sleep(50); final long t2Millis = System.currentTimeMillis(); Thread.sleep(50); pool.returnObject(s1); Thread.sleep(50); final long t3Millis = System.currentTimeMillis(); Thread.sleep(50); pool.borrowObject(); Thread.sleep(50); final long t4Millis = System.currentTimeMillis(); final Set strings = pool.listAllObjects(); assertEquals(1, strings.size()); final DefaultPooledObjectInfo s1Info = strings.iterator().next(); final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); assertTrue(s1Info.getCreateTime() > t1Millis); assertEquals(sdf.format(Long.valueOf(s1Info.getCreateTime())), s1Info.getCreateTimeFormatted()); assertTrue(s1Info.getCreateTime() < t2Millis); assertTrue(s1Info.getLastReturnTime() > t2Millis); assertEquals(sdf.format(Long.valueOf(s1Info.getLastReturnTime())), s1Info.getLastReturnTimeFormatted()); assertTrue(s1Info.getLastReturnTime() < t3Millis); assertTrue(s1Info.getLastBorrowTime() > t3Millis); assertEquals(sdf.format(Long.valueOf(s1Info.getLastBorrowTime())), s1Info.getLastBorrowTimeFormatted()); assertTrue(s1Info.getLastBorrowTime() < t4Millis); } } } TestEvictionConfig.java000066400000000000000000000047641405425132200341250ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import java.time.Duration; import org.junit.jupiter.api.Test; /** * Tests for {@link EvictionConfig}. */ public class TestEvictionConfig { @Test public void testConstructor1s() { final EvictionConfig config = new EvictionConfig(Duration.ofMillis(1), Duration.ofMillis(1), 1); assertEquals(1, config.getIdleEvictTime()); assertEquals(1, config.getIdleEvictTimeDuration().toMillis()); assertEquals(1, config.getIdleSoftEvictTime()); assertEquals(1, config.getIdleSoftEvictTimeDuration().toMillis()); assertEquals(1, config.getMinIdle()); } @Test public void testConstructorZerosDurations() { final EvictionConfig config = new EvictionConfig(Duration.ZERO, Duration.ZERO, 0); assertEquals(Long.MAX_VALUE, config.getIdleEvictTime()); assertEquals(Long.MAX_VALUE, config.getIdleEvictTimeDuration().toMillis()); assertEquals(Long.MAX_VALUE, config.getIdleSoftEvictTime()); assertEquals(Long.MAX_VALUE, config.getIdleSoftEvictTimeDuration().toMillis()); assertEquals(0, config.getMinIdle()); } @Test public void testConstructorZerosMillis() { @SuppressWarnings("deprecation") final EvictionConfig config = new EvictionConfig(0, 0, 0); assertEquals(Long.MAX_VALUE, config.getIdleEvictTime()); assertEquals(Long.MAX_VALUE, config.getIdleEvictTimeDuration().toMillis()); assertEquals(Long.MAX_VALUE, config.getIdleSoftEvictTime()); assertEquals(Long.MAX_VALUE, config.getIdleSoftEvictTimeDuration().toMillis()); assertEquals(0, config.getMinIdle()); } } TestEvictionTimer.java000066400000000000000000000116441405425132200337730ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.lang.reflect.Field; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadPoolExecutor; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.junit.jupiter.api.Test; /** * Tests for {@link EvictionTimer}. */ public class TestEvictionTimer { @Test public void testStartStopEvictionTimer() throws Exception { try (final GenericObjectPool pool = new GenericObjectPool<>(new BasePooledObjectFactory() { @Override public String create() throws Exception { return null; } @Override public PooledObject wrap(final String obj) { return new DefaultPooledObject<>(obj); } })) { // Start evictor #1 final BaseGenericObjectPool.Evictor evictor1 = pool.new Evictor(); EvictionTimer.schedule(evictor1, TestConstants.ONE_MINUTE, TestConstants.ONE_MINUTE); // Assert that eviction objects are correctly allocated // 1 - the evictor timer task is created final Field evictorTaskFutureField = evictor1.getClass().getDeclaredField("scheduledFuture"); evictorTaskFutureField.setAccessible(true); ScheduledFuture sf = (ScheduledFuture) evictorTaskFutureField.get(evictor1); assertFalse(sf.isCancelled()); // 2- and, the eviction action is added to executor thread pool final Field evictorExecutorField = EvictionTimer.class.getDeclaredField("executor"); evictorExecutorField.setAccessible(true); final ThreadPoolExecutor evictionExecutor = (ThreadPoolExecutor) evictorExecutorField.get(null); assertEquals(2, evictionExecutor.getQueue().size()); // Reaper plus one eviction task assertEquals(1, EvictionTimer.getNumTasks()); // Start evictor #2 final BaseGenericObjectPool.Evictor evictor2 = pool.new Evictor(); EvictionTimer.schedule(evictor2, TestConstants.ONE_MINUTE, TestConstants.ONE_MINUTE); // Assert that eviction objects are correctly allocated // 1 - the evictor timer task is created sf = (ScheduledFuture) evictorTaskFutureField.get(evictor2); assertFalse(sf.isCancelled()); // 2- and, the eviction action is added to executor thread pool assertEquals(3, evictionExecutor.getQueue().size()); // Reaper plus 2 eviction tasks assertEquals(2, EvictionTimer.getNumTasks()); // Stop evictor #1 EvictionTimer.cancel(evictor1, BaseObjectPoolConfig.DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT, false); // Assert that eviction objects are correctly cleaned // 1 - the evictor timer task is cancelled sf = (ScheduledFuture) evictorTaskFutureField.get(evictor1); assertTrue(sf.isCancelled()); // 2- and, the eviction action is removed from executor thread pool final ThreadPoolExecutor evictionExecutorOnStop = (ThreadPoolExecutor) evictorExecutorField.get(null); assertEquals(2, evictionExecutorOnStop.getQueue().size()); assertEquals(1, EvictionTimer.getNumTasks()); // Stop evictor #2 EvictionTimer.cancel(evictor2, BaseObjectPoolConfig.DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT, false); // Assert that eviction objects are correctly cleaned // 1 - the evictor timer task is cancelled sf = (ScheduledFuture) evictorTaskFutureField.get(evictor2); assertTrue(sf.isCancelled()); // 2- and, the eviction thread pool executor is freed assertNull(evictorExecutorField.get(null)); assertEquals(0, EvictionTimer.getNumTasks()); } } }TestGenericKeyedObjectPool.java000066400000000000000000003067761405425132200355460ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.management.ManagementFactory; import java.time.Duration; import java.util.ArrayList; import java.util.HashSet; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import javax.management.MBeanServer; import javax.management.ObjectName; import org.apache.commons.pool2.BaseKeyedPooledObjectFactory; import org.apache.commons.pool2.KeyedObjectPool; import org.apache.commons.pool2.KeyedPooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.TestKeyedObjectPool; import org.apache.commons.pool2.VisitTracker; import org.apache.commons.pool2.VisitTrackerFactory; import org.apache.commons.pool2.Waiter; import org.apache.commons.pool2.WaiterFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; /** */ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool { private static class DaemonThreadFactory implements ThreadFactory { @Override public Thread newThread(final Runnable r) { final Thread t = new Thread(r); t.setDaemon(true); return t; } } private static class DummyFactory extends BaseKeyedPooledObjectFactory { @Override public Object create(final Object key) throws Exception { return null; } @Override public PooledObject wrap(final Object value) { return new DefaultPooledObject<>(value); } } /** * Factory that creates HashSets. Note that this means * 0) All instances are initially equal (not discernible by equals) * 1) Instances are mutable and mutation can cause change in identity / hashcode. */ private static final class HashSetFactory extends BaseKeyedPooledObjectFactory> { @Override public HashSet create(final String key) throws Exception { return new HashSet<>(); } @Override public PooledObject> wrap(final HashSet value) { return new DefaultPooledObject<>(value); } } /** * Attempts to invalidate an object, swallowing IllegalStateException. */ static class InvalidateThread implements Runnable { private final String obj; private final KeyedObjectPool pool; private final String key; private boolean done = false; public InvalidateThread(final KeyedObjectPool pool, final String key, final String obj) { this.obj = obj; this.pool = pool; this.key = key; } public boolean complete() { return done; } @Override public void run() { try { pool.invalidateObject(key, obj); } catch (final IllegalStateException ex) { // Ignore } catch (final Exception ex) { fail("Unexpected exception " + ex.toString()); } finally { done = true; } } } private static class ObjectFactory extends BaseKeyedPooledObjectFactory { @Override public Object create(final Integer key) throws Exception { return new Object(); } @Override public PooledObject wrap(final Object value) { return new DefaultPooledObject<>(value); } } public static class SimpleFactory implements KeyedPooledObjectFactory { volatile int counter = 0; final boolean valid; int activeCount = 0; int validateCounter = 0; boolean evenValid = true; boolean oddValid = true; boolean enableValidation = false; long destroyLatency = 0; long makeLatency = 0; long validateLatency = 0; volatile int maxTotalPerKey = Integer.MAX_VALUE; boolean exceptionOnPassivate = false; boolean exceptionOnActivate = false; boolean exceptionOnDestroy = false; boolean exceptionOnCreate = false; public SimpleFactory() { this(true); } public SimpleFactory(final boolean valid) { this.valid = valid; } @Override public void activateObject(final K key, final PooledObject obj) throws Exception { if (exceptionOnActivate && !(validateCounter++%2 == 0 ? evenValid : oddValid)) { throw new Exception(); } } @Override public void destroyObject(final K key, final PooledObject obj) throws Exception { doWait(destroyLatency); synchronized(this) { activeCount--; } if (exceptionOnDestroy) { throw new Exception(); } } private void doWait(final long latency) { try { Thread.sleep(latency); } catch (final InterruptedException ex) { // ignore } } @Override public PooledObject makeObject(final K key) throws Exception { if (exceptionOnCreate) { throw new Exception(); } doWait(makeLatency); String out = null; synchronized(this) { activeCount++; if (activeCount > maxTotalPerKey) { throw new IllegalStateException( "Too many active instances: " + activeCount); } out = String.valueOf(key) + String.valueOf(counter++); } return new DefaultPooledObject<>(out); } @Override public void passivateObject(final K key, final PooledObject obj) throws Exception { if (exceptionOnPassivate) { throw new Exception(); } } public void setDestroyLatency(final long destroyLatency) { this.destroyLatency = destroyLatency; } void setEvenValid(final boolean valid) { evenValid = valid; } public void setMakeLatency(final long makeLatency) { this.makeLatency = makeLatency; } public void setMaxTotalPerKey(final int maxTotalPerKey) { this.maxTotalPerKey = maxTotalPerKey; } public void setThrowExceptionOnActivate(final boolean b) { exceptionOnActivate = b; } public void setThrowExceptionOnDestroy(final boolean b) { exceptionOnDestroy = b; } public void setThrowExceptionOnPassivate(final boolean b) { exceptionOnPassivate = b; } void setValid(final boolean valid) { evenValid = valid; oddValid = valid; } public void setValidateLatency(final long validateLatency) { this.validateLatency = validateLatency; } public void setValidationEnabled(final boolean b) { enableValidation = b; } @Override public boolean validateObject(final K key, final PooledObject obj) { doWait(validateLatency); if (enableValidation) { return validateCounter++%2 == 0 ? evenValid : oddValid; } return valid; } } private static class SimplePerKeyFactory extends BaseKeyedPooledObjectFactory { final ConcurrentHashMap map = new ConcurrentHashMap<>(); @Override public Object create(final Object key) throws Exception { int counter = 0; final AtomicInteger Counter = map.get(key); if(null != Counter) { counter = Counter.incrementAndGet(); } else { map.put(key, new AtomicInteger(0)); counter = 0; } return String.valueOf(key) + String.valueOf(counter); } @Override public PooledObject wrap(final Object value) { return new DefaultPooledObject<>(value); } } /* * Very simple test thread that just tries to borrow an object from * the provided pool with the specified key and returns it */ static class SimpleTestThread implements Runnable { private final KeyedObjectPool _pool; private final String _key; public SimpleTestThread(final KeyedObjectPool pool, final String key) { _pool = pool; _key = key; } @Override public void run() { try { final T obj = _pool.borrowObject(_key); _pool.returnObject(_key, obj); } catch (final Exception e) { // Ignore } } } /* * DefaultEvictionPolicy modified to add latency */ private static class SlowEvictionPolicy extends DefaultEvictionPolicy { private final long delay; /** * Create SlowEvictionPolicy with the given delay in ms * * @param delay number of ms of latency to inject in evict */ public SlowEvictionPolicy(final long delay) { this.delay = delay; } @Override public boolean evict(final EvictionConfig config, final PooledObject underTest, final int idleCount) { try { Thread.sleep(delay); } catch (final InterruptedException e) { // ignore } return super.evict(config, underTest, idleCount); } } static class TestThread implements Runnable { private final java.util.Random _random = new java.util.Random(); /** GKOP to hit */ private final KeyedObjectPool _pool; /** number of borrow/return iterations */ private final int _iter; /** delay before borrow */ private final int _startDelay; /** delay before return */ private final int _holdTime; /** whether or not delays are random (with max = configured values) */ private final boolean _randomDelay; /** expected object */ private final T _expectedObject; /** key used in borrow / return sequence - null means random */ private final String _key; private volatile boolean _complete = false; private volatile boolean _failed = false; private volatile Exception _exception; public TestThread(final KeyedObjectPool pool) { this(pool, 100, 50, 50, true, null, null); } public TestThread(final KeyedObjectPool pool, final int iter) { this(pool, iter, 50, 50, true, null, null); } public TestThread(final KeyedObjectPool pool, final int iter, final int delay) { this(pool, iter, delay, delay, true, null, null); } public TestThread(final KeyedObjectPool pool, final int iter, final int startDelay, final int holdTime, final boolean randomDelay, final T expectedObject, final String key) { _pool = pool; _iter = iter; _startDelay = startDelay; _holdTime = holdTime; _randomDelay = randomDelay; _expectedObject = expectedObject; _key = key; } public boolean complete() { return _complete; } public boolean failed() { return _failed; } @Override public void run() { for(int i=0;i<_iter;i++) { final String key = _key == null ? String.valueOf(_random.nextInt(3)) : _key; try { Thread.sleep(_randomDelay ? _random.nextInt(_startDelay) : _startDelay); } catch(final InterruptedException e) { // ignored } T obj = null; try { obj = _pool.borrowObject(key); } catch(final Exception e) { _exception = e; _failed = true; _complete = true; break; } if (_expectedObject != null && !_expectedObject.equals(obj)) { _exception = new Exception("Expected: "+_expectedObject+ " found: "+obj); _failed = true; _complete = true; break; } try { Thread.sleep(_randomDelay ? _random.nextInt(_holdTime) : _holdTime); } catch(final InterruptedException e) { // ignored } try { _pool.returnObject(key,obj); } catch(final Exception e) { _exception = e; _failed = true; _complete = true; break; } } _complete = true; } } /* * Very simple test thread that just tries to borrow an object from * the provided pool with the specified key and returns it after a wait */ static class WaitingTestThread extends Thread { private final KeyedObjectPool _pool; private final String _key; private final long _pause; private Throwable _thrown; private long preBorrowMillis; // just before borrow private long postBorrowMillis; // borrow returned private long postReturnMillis; // after object was returned private long endedMillis; private String objectId; public WaitingTestThread(final KeyedObjectPool pool, final String key, final long pause) { _pool = pool; _key = key; _pause = pause; _thrown = null; } @Override public void run() { try { preBorrowMillis = System.currentTimeMillis(); final String obj = _pool.borrowObject(_key); objectId = obj; postBorrowMillis = System.currentTimeMillis(); Thread.sleep(_pause); _pool.returnObject(_key, obj); postReturnMillis = System.currentTimeMillis(); } catch (final Exception e) { _thrown = e; } finally{ endedMillis = System.currentTimeMillis(); } } } private static final Integer KEY_ZERO = Integer.valueOf(0); private static final Integer KEY_ONE = Integer.valueOf(1); private static final Integer KEY_TWO = Integer.valueOf(2); private static final boolean DISPLAY_THREAD_DETAILS= Boolean.parseBoolean(System.getProperty("TestGenericKeyedObjectPool.display.thread.details", "false")); // To pass this to a Maven test, use: // mvn test -DargLine="-DTestGenericKeyedObjectPool.display.thread.details=true" // @see https://issues.apache.org/jira/browse/SUREFIRE-121 /** setUp(): {@code new GenericKeyedObjectPool(factory)} */ private GenericKeyedObjectPool gkoPool = null; /** setUp(): {@code new SimpleFactory()} */ private SimpleFactory simpleFactory = null; private void checkEvictionOrder(final boolean lifo) throws Exception { final SimpleFactory intFactory = new SimpleFactory<>(); try (final GenericKeyedObjectPool intPool = new GenericKeyedObjectPool<>(intFactory)) { intPool.setNumTestsPerEvictionRun(2); intPool.setMinEvictableIdleTime(Duration.ofMillis(100)); intPool.setLifo(lifo); for (int i = 0; i < 3; i++) { final Integer key = Integer.valueOf(i); for (int j = 0; j < 5; j++) { intPool.addObject(key); } } // Make all evictable Thread.sleep(200); /* * Initial state (Key, Object) pairs in order of age: * * (0,0), (0,1), (0,2), (0,3), (0,4) (1,5), (1,6), (1,7), (1,8), (1,9) (2,10), (2,11), (2,12), (2,13), * (2,14) */ intPool.evict(); // Kill (0,0),(0,1) assertEquals(3, intPool.getNumIdle(KEY_ZERO)); final String objZeroA = intPool.borrowObject(KEY_ZERO); assertTrue(lifo ? objZeroA.equals("04") : objZeroA.equals("02")); assertEquals(2, intPool.getNumIdle(KEY_ZERO)); final String objZeroB = intPool.borrowObject(KEY_ZERO); assertEquals("03", objZeroB); assertEquals(1, intPool.getNumIdle(KEY_ZERO)); intPool.evict(); // Kill remaining 0 survivor and (1,5) assertEquals(0, intPool.getNumIdle(KEY_ZERO)); assertEquals(4, intPool.getNumIdle(KEY_ONE)); final String objOneA = intPool.borrowObject(KEY_ONE); assertTrue(lifo ? objOneA.equals("19") : objOneA.equals("16")); assertEquals(3, intPool.getNumIdle(KEY_ONE)); final String objOneB = intPool.borrowObject(KEY_ONE); assertTrue(lifo ? objOneB.equals("18") : objOneB.equals("17")); assertEquals(2, intPool.getNumIdle(KEY_ONE)); intPool.evict(); // Kill remaining 1 survivors assertEquals(0, intPool.getNumIdle(KEY_ONE)); intPool.evict(); // Kill (2,10), (2,11) assertEquals(3, intPool.getNumIdle(KEY_TWO)); final String objTwoA = intPool.borrowObject(KEY_TWO); assertTrue(lifo ? objTwoA.equals("214") : objTwoA.equals("212")); assertEquals(2, intPool.getNumIdle(KEY_TWO)); intPool.evict(); // All dead now assertEquals(0, intPool.getNumIdle(KEY_TWO)); intPool.evict(); // Should do nothing - make sure no exception // Currently 2 zero, 2 one and 1 two active. Return them intPool.returnObject(KEY_ZERO, objZeroA); intPool.returnObject(KEY_ZERO, objZeroB); intPool.returnObject(KEY_ONE, objOneA); intPool.returnObject(KEY_ONE, objOneB); intPool.returnObject(KEY_TWO, objTwoA); // Remove all idle objects intPool.clear(); // Reload intPool.setMinEvictableIdleTime(Duration.ofMillis(500)); intFactory.counter = 0; // Reset counter for (int i = 0; i < 3; i++) { final Integer key = Integer.valueOf(i); for (int j = 0; j < 5; j++) { intPool.addObject(key); } Thread.sleep(200); } // 0's are evictable, others not intPool.evict(); // Kill (0,0),(0,1) assertEquals(3, intPool.getNumIdle(KEY_ZERO)); intPool.evict(); // Kill (0,2),(0,3) assertEquals(1, intPool.getNumIdle(KEY_ZERO)); intPool.evict(); // Kill (0,4), leave (1,5) assertEquals(0, intPool.getNumIdle(KEY_ZERO)); assertEquals(5, intPool.getNumIdle(KEY_ONE)); assertEquals(5, intPool.getNumIdle(KEY_TWO)); intPool.evict(); // (1,6), (1,7) assertEquals(5, intPool.getNumIdle(KEY_ONE)); assertEquals(5, intPool.getNumIdle(KEY_TWO)); intPool.evict(); // (1,8), (1,9) assertEquals(5, intPool.getNumIdle(KEY_ONE)); assertEquals(5, intPool.getNumIdle(KEY_TWO)); intPool.evict(); // (2,10), (2,11) assertEquals(5, intPool.getNumIdle(KEY_ONE)); assertEquals(5, intPool.getNumIdle(KEY_TWO)); intPool.evict(); // (2,12), (2,13) assertEquals(5, intPool.getNumIdle(KEY_ONE)); assertEquals(5, intPool.getNumIdle(KEY_TWO)); intPool.evict(); // (2,14), (1,5) assertEquals(5, intPool.getNumIdle(KEY_ONE)); assertEquals(5, intPool.getNumIdle(KEY_TWO)); Thread.sleep(200); // Ones now timed out intPool.evict(); // kill (1,6), (1,7) - (1,5) missed assertEquals(3, intPool.getNumIdle(KEY_ONE)); assertEquals(5, intPool.getNumIdle(KEY_TWO)); final String obj = intPool.borrowObject(KEY_ONE); if (lifo) { assertEquals("19", obj); } else { assertEquals("15", obj); } } } private void checkEvictorVisiting(final boolean lifo) throws Exception { VisitTrackerFactory trackerFactory = new VisitTrackerFactory<>(); try (GenericKeyedObjectPool> intPool = new GenericKeyedObjectPool<>( trackerFactory)) { intPool.setNumTestsPerEvictionRun(2); intPool.setMinEvictableIdleTime(Duration.ofMillis(-1)); intPool.setTestWhileIdle(true); intPool.setLifo(lifo); intPool.setTestOnReturn(false); intPool.setTestOnBorrow(false); for (int i = 0; i < 3; i++) { trackerFactory.resetId(); final Integer key = Integer.valueOf(i); for (int j = 0; j < 8; j++) { intPool.addObject(key); } } intPool.evict(); // Visit oldest 2 - 00 and 01 VisitTracker obj = intPool.borrowObject(KEY_ZERO); intPool.returnObject(KEY_ZERO, obj); obj = intPool.borrowObject(KEY_ZERO); intPool.returnObject(KEY_ZERO, obj); // borrow, return, borrow, return // FIFO will move 0 and 1 to end - 2,3,4,5,6,7,0,1 // LIFO, 7 out, then in, then out, then in - 7,6,5,4,3,2,1,0 intPool.evict(); // Should visit 02 and 03 in either case for (int i = 0; i < 8; i++) { final VisitTracker tracker = intPool.borrowObject(KEY_ZERO); if (tracker.getId() >= 4) { assertEquals( 0, tracker.getValidateCount(),"Unexpected instance visited " + tracker.getId()); } else { assertEquals( 1, tracker.getValidateCount(),"Instance " + tracker.getId() + " visited wrong number of times."); } } // 0's are all out intPool.setNumTestsPerEvictionRun(3); intPool.evict(); // 10, 11, 12 intPool.evict(); // 13, 14, 15 obj = intPool.borrowObject(KEY_ONE); intPool.returnObject(KEY_ONE, obj); obj = intPool.borrowObject(KEY_ONE); intPool.returnObject(KEY_ONE, obj); obj = intPool.borrowObject(KEY_ONE); intPool.returnObject(KEY_ONE, obj); // borrow, return, borrow, return // FIFO 3,4,5,^,6,7,0,1,2 // LIFO 7,6,^,5,4,3,2,1,0 // In either case, pointer should be at 6 intPool.evict(); // LIFO - 16, 17, 20 // FIFO - 16, 17, 10 intPool.evict(); // LIFO - 21, 22, 23 // FIFO - 11, 12, 20 intPool.evict(); // LIFO - 24, 25, 26 // FIFO - 21, 22, 23 intPool.evict(); // LIFO - 27, 10, 11 // FIFO - 24, 25, 26 for (int i = 0; i < 8; i++) { final VisitTracker tracker = intPool.borrowObject(KEY_ONE); if ((lifo && tracker.getId() > 1) || (!lifo && tracker.getId() > 2)) { assertEquals( 1, tracker.getValidateCount(),"Instance " + tracker.getId() + " visited wrong number of times."); } else { assertEquals( 2, tracker.getValidateCount(),"Instance " + tracker.getId() + " visited wrong number of times."); } } } // Randomly generate some pools with random numTests // and make sure evictor cycles through elements appropriately final int[] smallPrimes = { 2, 3, 5, 7 }; final Random random = new Random(); random.setSeed(System.currentTimeMillis()); for (int i = 0; i < smallPrimes.length; i++) { for (int j = 0; j < 5; j++) {// Try the tests a few times // Can't use clear as some objects are still active so create // a new pool trackerFactory = new VisitTrackerFactory<>(); try (GenericKeyedObjectPool> intPool = new GenericKeyedObjectPool<>( trackerFactory)) { intPool.setMaxIdlePerKey(-1); intPool.setMaxTotalPerKey(-1); intPool.setNumTestsPerEvictionRun(smallPrimes[i]); intPool.setMinEvictableIdleTime(Duration.ofMillis(-1)); intPool.setTestWhileIdle(true); intPool.setLifo(lifo); intPool.setTestOnReturn(false); intPool.setTestOnBorrow(false); final int zeroLength = 10 + random.nextInt(20); for (int k = 0; k < zeroLength; k++) { intPool.addObject(KEY_ZERO); } final int oneLength = 10 + random.nextInt(20); for (int k = 0; k < oneLength; k++) { intPool.addObject(KEY_ONE); } final int twoLength = 10 + random.nextInt(20); for (int k = 0; k < twoLength; k++) { intPool.addObject(KEY_TWO); } // Choose a random number of evictor runs final int runs = 10 + random.nextInt(50); for (int k = 0; k < runs; k++) { intPool.evict(); } // Total instances in pool final int totalInstances = zeroLength + oneLength + twoLength; // Number of times evictor should have cycled through pools final int cycleCount = (runs * intPool.getNumTestsPerEvictionRun()) / totalInstances; // Look at elements and make sure they are visited cycleCount // or cycleCount + 1 times VisitTracker tracker = null; int visitCount = 0; for (int k = 0; k < zeroLength; k++) { tracker = intPool.borrowObject(KEY_ZERO); visitCount = tracker.getValidateCount(); if (visitCount < cycleCount || visitCount > cycleCount + 1) { fail(formatSettings("ZERO", "runs", runs, "lifo", lifo, "i", i, "j", j, "k", k, "visitCount", visitCount, "cycleCount", cycleCount, "totalInstances", totalInstances, zeroLength, oneLength, twoLength)); } } for (int k = 0; k < oneLength; k++) { tracker = intPool.borrowObject(KEY_ONE); visitCount = tracker.getValidateCount(); if (visitCount < cycleCount || visitCount > cycleCount + 1) { fail(formatSettings("ONE", "runs", runs, "lifo", lifo, "i", i, "j", j, "k", k, "visitCount", visitCount, "cycleCount", cycleCount, "totalInstances", totalInstances, zeroLength, oneLength, twoLength)); } } final int[] visits = new int[twoLength]; for (int k = 0; k < twoLength; k++) { tracker = intPool.borrowObject(KEY_TWO); visitCount = tracker.getValidateCount(); visits[k] = visitCount; if (visitCount < cycleCount || visitCount > cycleCount + 1) { final StringBuilder sb = new StringBuilder("Visits:"); for (int l = 0; l <= k; l++) { sb.append(visits[l]).append(' '); } fail(formatSettings("TWO " + sb.toString(), "runs", runs, "lifo", lifo, "i", i, "j", j, "k", k, "visitCount", visitCount, "cycleCount", cycleCount, "totalInstances", totalInstances, zeroLength, oneLength, twoLength)); } } } } } } private String formatSettings(final String title, final String s, final int i, final String s0, final boolean b0, final String s1, final int i1, final String s2, final int i2, final String s3, final int i3, final String s4, final int i4, final String s5, final int i5, final String s6, final int i6, final int zeroLength, final int oneLength, final int twoLength){ final StringBuilder sb = new StringBuilder(80); sb.append(title).append(' '); sb.append(s).append('=').append(i).append(' '); sb.append(s0).append('=').append(b0).append(' '); sb.append(s1).append('=').append(i1).append(' '); sb.append(s2).append('=').append(i2).append(' '); sb.append(s3).append('=').append(i3).append(' '); sb.append(s4).append('=').append(i4).append(' '); sb.append(s5).append('=').append(i5).append(' '); sb.append(s6).append('=').append(i6).append(' '); sb.append("Lengths=").append(zeroLength).append(',').append(oneLength).append(',').append(twoLength).append(' '); return sb.toString(); } private String getExceptionTrace(final Throwable t){ final StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); return sw.toString(); } @Override protected Object getNthObject(final Object key, final int n) { return String.valueOf(key) + String.valueOf(n); } @Override protected boolean isFifo() { return false; } @Override protected boolean isLifo() { return true; } @Override protected KeyedObjectPool makeEmptyPool(final int minCapacity) { final KeyedPooledObjectFactory perKeyFactory = new SimplePerKeyFactory(); final GenericKeyedObjectPool perKeyPool = new GenericKeyedObjectPool<>(perKeyFactory); perKeyPool.setMaxTotalPerKey(minCapacity); perKeyPool.setMaxIdlePerKey(minCapacity); return perKeyPool; } @Override protected KeyedObjectPool makeEmptyPool(final KeyedPooledObjectFactory fac) { return new GenericKeyedObjectPool<>(fac); } @Override protected Object makeKey(final int n) { return String.valueOf(n); } /** * Kicks off {@code numThreads} test threads, each of which will go * through {@code iterations} borrow-return cycles with random delay * times <= delay in between. * * @param Type of object in pool * @param numThreads Number of test threads * @param iterations Number of iterations for each thread * @param delay Maximum delay between iterations * @param gkopPool The keyed object pool to use */ public void runTestThreads(final int numThreads, final int iterations, final int delay, final GenericKeyedObjectPool gkopPool) { final ArrayList> threads = new ArrayList<>(); for(int i=0;i testThread = new TestThread<>(gkopPool, iterations, delay); threads.add(testThread); final Thread t = new Thread(testThread); t.start(); } for (final TestThread testThread : threads) { while(!(testThread.complete())) { try { Thread.sleep(500L); } catch(final InterruptedException e) { // ignored } } if(testThread.failed()) { fail("Thread failed: " + threads.indexOf(testThread) + "\n" + getExceptionTrace(testThread._exception)); } } } @BeforeEach public void setUp() throws Exception { simpleFactory = new SimpleFactory<>(); gkoPool = new GenericKeyedObjectPool<>(simpleFactory); } @Override @AfterEach public void tearDown() throws Exception { super.tearDown(); final ObjectName jmxName = gkoPool.getJmxName(); final String poolName = Objects.toString(jmxName, null); gkoPool.clear(); gkoPool.close(); gkoPool = null; simpleFactory = null; final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); final Set result = mbs.queryNames(new ObjectName( "org.apache.commoms.pool2:type=GenericKeyedObjectPool,*"), null); // There should be no registered pools at this point final int registeredPoolCount = result.size(); final StringBuilder msg = new StringBuilder("Current pool is: "); msg.append(poolName); msg.append(" Still open pools are: "); for (final ObjectName name : result) { // Clean these up ready for the next test msg.append(name.toString()); msg.append(" created via\n"); msg.append(mbs.getAttribute(name, "CreationStackTrace")); msg.append('\n'); mbs.unregisterMBean(name); } assertEquals( 0, registeredPoolCount, msg.toString()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testBlockedKeyDoesNotBlockPool() throws Exception { gkoPool.setBlockWhenExhausted(true); gkoPool.setMaxWaitMillis(5000); gkoPool.setMaxTotalPerKey(1); gkoPool.setMaxTotal(-1); gkoPool.borrowObject("one"); final long startMillis = System.currentTimeMillis(); // Needs to be in a separate thread as this will block final Runnable simple = new SimpleTestThread<>(gkoPool, "one"); (new Thread(simple)).start(); // This should be almost instant. If it isn't it means this thread got // stuck behind the thread created above which is bad. // Give other thread a chance to start Thread.sleep(1000); gkoPool.borrowObject("two"); final long endMillis = System.currentTimeMillis(); // If it fails it will be more than 4000ms (5000 less the 1000 sleep) // If it passes it should be almost instant // Use 3000ms as the threshold - should avoid timing issues on most // (all? platforms) assertTrue((endMillis - startMillis) < 4000, "Elapsed time: " + (endMillis - startMillis) + " should be less than 4000"); } /* * Note: This test relies on timing for correct execution. There *should* be * enough margin for this to work correctly on most (all?) systems but be * aware of this if you see a failure of this test. */ @SuppressWarnings({ "rawtypes" }) @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testBorrowObjectFairness() throws Exception { final int numThreads = 40; final int maxTotal = 40; final GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig<>(); config.setMaxTotalPerKey(maxTotal); config.setFairness(true); config.setLifo(false); config.setMaxIdlePerKey(maxTotal); gkoPool = new GenericKeyedObjectPool<>(simpleFactory, config); // Exhaust the pool final String[] objects = new String[maxTotal]; for (int i = 0; i < maxTotal; i++) { objects[i] = gkoPool.borrowObject("0"); } // Start and park threads waiting to borrow objects final TestThread[] threads = new TestThread[numThreads]; for(int i=0;i(gkoPool, 1, 0, 2000, false, "0" + String.valueOf(i % maxTotal), "0"); final Thread t = new Thread(threads[i]); t.start(); // Short delay to ensure threads start in correct order try { Thread.sleep(10); } catch (final InterruptedException e) { fail(e.toString()); } } // Return objects, other threads should get served in order for (int i = 0; i < maxTotal; i++) { gkoPool.returnObject("0", objects[i]); } // Wait for threads to finish for(int i=0;i waiterFactory = new WaiterFactory<>(0, 20, 0, 0, 0, 0, 50, 5, 0); try (final GenericKeyedObjectPool waiterPool = new GenericKeyedObjectPool<>(waiterFactory)) { waiterPool.setMaxTotalPerKey(5); waiterPool.setMaxTotal(50); waiterPool.setLifo(false); // Load the pool with idle instances - 5 each for 10 keys for (int i = 0; i < 10; i++) { final String key = Integer.toString(i); for (int j = 0; j < 5; j++) { waiterPool.addObject(key); } // Make sure order is maintained Thread.sleep(20); } // Now set up a race - one thread wants a new instance, triggering clearOldest // Other goes after an element on death row // See if we end up with dead man walking final SimpleTestThread t2 = new SimpleTestThread<>(waiterPool, "51"); final Thread thread2 = new Thread(t2); thread2.start(); // Triggers clearOldest, killing all of the 0's and the 2 oldest 1's Thread.sleep(50); // Wait for clearOldest to kick off, but not long enough to reach the 1's final Waiter waiter = waiterPool.borrowObject("1"); Thread.sleep(200); // Wait for execution to happen waiterPool.returnObject("1", waiter); // Will throw IllegalStateException if dead } } // POOL-259 @Test public void testClientWaitStats() throws Exception { final SimpleFactory factory = new SimpleFactory<>(); // Give makeObject a little latency factory.setMakeLatency(200); try (final GenericKeyedObjectPool pool = new GenericKeyedObjectPool<>(factory, new GenericKeyedObjectPoolConfig<>())) { final String s = pool.borrowObject("one"); // First borrow waits on create, so wait time should be at least 200 ms // Allow 100ms error in clock times assertTrue(pool.getMaxBorrowWaitTimeMillis() >= 100); assertTrue(pool.getMeanBorrowWaitTimeMillis() >= 100); pool.returnObject("one", s); pool.borrowObject("one"); // Second borrow does not have to wait on create, average should be about 100 assertTrue(pool.getMaxBorrowWaitTimeMillis() > 100); assertTrue(pool.getMeanBorrowWaitTimeMillis() < 200); assertTrue(pool.getMeanBorrowWaitTimeMillis() > 20); } } /** * POOL-231 - verify that concurrent invalidates of the same object do not * corrupt pool destroyCount. * * @throws Exception May occur in some failure modes */ @Test public void testConcurrentInvalidate() throws Exception { // Get allObjects and idleObjects loaded with some instances final int nObjects = 1000; final String key = "one"; gkoPool.setMaxTotal(nObjects); gkoPool.setMaxTotalPerKey(nObjects); gkoPool.setMaxIdlePerKey(nObjects); final String [] obj = new String[nObjects]; for (int i = 0; i < nObjects; i++) { obj[i] = gkoPool.borrowObject(key); } for (int i = 0; i < nObjects; i++) { if (i % 2 == 0) { gkoPool.returnObject(key, obj[i]); } } final int nThreads = 20; final int nIterations = 60; final InvalidateThread[] threads = new InvalidateThread[nThreads]; // Randomly generated list of distinct invalidation targets final ArrayList targets = new ArrayList<>(); final Random random = new Random(); for (int j = 0; j < nIterations; j++) { // Get a random invalidation target Integer targ = Integer.valueOf(random.nextInt(nObjects)); while (targets.contains(targ)) { targ = Integer.valueOf(random.nextInt(nObjects)); } targets.add(targ); // Launch nThreads threads all trying to invalidate the target for (int i = 0; i < nThreads; i++) { threads[i] = new InvalidateThread(gkoPool,key, obj[targ.intValue()]); } for (int i = 0; i < nThreads; i++) { new Thread(threads[i]).start(); } boolean done = false; while (!done) { done = true; for (int i = 0; i < nThreads; i++) { done = done && threads[i].complete(); } Thread.sleep(100); } } assertEquals(nIterations, gkoPool.getDestroyedCount()); } @Test public void testConstructorNullFactory() { // add dummy assert (won't be invoked because of IAE) to avoid "unused" warning assertThrows(IllegalArgumentException.class, () -> new GenericKeyedObjectPool<>(null)); } @SuppressWarnings("deprecation") @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testConstructors() throws Exception { // Make constructor arguments all different from defaults final int maxTotalPerKey = 1; final int minIdle = 2; final long maxWait = 3; final int maxIdle = 4; final int maxTotal = 5; final long minEvictableIdleTimeMillis = 6; final int numTestsPerEvictionRun = 7; final boolean testOnBorrow = true; final boolean testOnReturn = true; final boolean testWhileIdle = true; final long timeBetweenEvictionRunsMillis = 8; final boolean blockWhenExhausted = false; final boolean lifo = false; final KeyedPooledObjectFactory dummyFactory = new DummyFactory(); try (GenericKeyedObjectPool objPool = new GenericKeyedObjectPool<>(dummyFactory)) { assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY, objPool.getMaxTotalPerKey()); assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY, objPool.getMaxIdlePerKey()); assertEquals(BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS, objPool.getMaxWaitMillis()); assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY, objPool.getMinIdlePerKey()); assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL, objPool.getMaxTotal()); // assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, objPool.getMinEvictableIdleTimeMillis()); assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME, objPool.getMinEvictableIdleTime()); // assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN, objPool.getNumTestsPerEvictionRun()); assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW), Boolean.valueOf(objPool.getTestOnBorrow())); assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN), Boolean.valueOf(objPool.getTestOnReturn())); assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE), Boolean.valueOf(objPool.getTestWhileIdle())); // assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, objPool.getTimeBetweenEvictionRunsMillis()); assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS, objPool.getTimeBetweenEvictionRuns()); // assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED), Boolean.valueOf(objPool.getBlockWhenExhausted())); assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(objPool.getLifo())); } final GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig<>(); config.setLifo(lifo); config.setMaxTotalPerKey(maxTotalPerKey); config.setMaxIdlePerKey(maxIdle); config.setMinIdlePerKey(minIdle); config.setMaxTotal(maxTotal); config.setMaxWaitMillis(maxWait); config.setMinEvictableIdleTime(Duration.ofMillis(minEvictableIdleTimeMillis)); config.setNumTestsPerEvictionRun(numTestsPerEvictionRun); config.setTestOnBorrow(testOnBorrow); config.setTestOnReturn(testOnReturn); config.setTestWhileIdle(testWhileIdle); config.setTimeBetweenEvictionRuns(Duration.ofMillis(timeBetweenEvictionRunsMillis)); config.setBlockWhenExhausted(blockWhenExhausted); try (GenericKeyedObjectPool objPool = new GenericKeyedObjectPool<>(dummyFactory, config)) { assertEquals(maxTotalPerKey, objPool.getMaxTotalPerKey()); assertEquals(maxIdle, objPool.getMaxIdlePerKey()); assertEquals(maxWait, objPool.getMaxWaitMillis()); assertEquals(minIdle, objPool.getMinIdlePerKey()); assertEquals(maxTotal, objPool.getMaxTotal()); assertEquals(minEvictableIdleTimeMillis, objPool.getMinEvictableIdleTimeMillis()); assertEquals(minEvictableIdleTimeMillis, objPool.getMinEvictableIdleTime().toMillis()); assertEquals(numTestsPerEvictionRun, objPool.getNumTestsPerEvictionRun()); assertEquals(Boolean.valueOf(testOnBorrow), Boolean.valueOf(objPool.getTestOnBorrow())); assertEquals(Boolean.valueOf(testOnReturn), Boolean.valueOf(objPool.getTestOnReturn())); assertEquals(Boolean.valueOf(testWhileIdle), Boolean.valueOf(objPool.getTestWhileIdle())); assertEquals(timeBetweenEvictionRunsMillis, objPool.getTimeBetweenEvictionRunsMillis()); assertEquals(timeBetweenEvictionRunsMillis, objPool.getTimeBetweenEvictionRuns().toMillis()); assertEquals(Boolean.valueOf(blockWhenExhausted), Boolean.valueOf(objPool.getBlockWhenExhausted())); assertEquals(Boolean.valueOf(lifo), Boolean.valueOf(objPool.getLifo())); } } /** * JIRA: POOL-270 - make sure constructor correctly sets run * frequency of evictor timer. */ @Test public void testContructorEvictionConfig() throws Exception { final GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig<>(); config.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); config.setMinEvictableIdleTime(Duration.ofMillis(50)); config.setNumTestsPerEvictionRun(5); try (final GenericKeyedObjectPool p = new GenericKeyedObjectPool<>(simpleFactory, config)) { for (int i = 0; i < 5; i++) { p.addObject("one"); } try { Thread.sleep(100); } catch (final InterruptedException e) { // ignore } assertEquals(5, p.getNumIdle("one")); try { Thread.sleep(500); } catch (final InterruptedException e) { // ignore } assertEquals(0, p.getNumIdle("one")); } } /** * Verifies that when a factory's makeObject produces instances that are not * discernible by equals, the pool can handle them. * * JIRA: POOL-283 */ @Test public void testEqualsIndiscernible() throws Exception { final HashSetFactory factory = new HashSetFactory(); try (final GenericKeyedObjectPool> pool = new GenericKeyedObjectPool<>(factory, new GenericKeyedObjectPoolConfig<>())) { final HashSet s1 = pool.borrowObject("a"); final HashSet s2 = pool.borrowObject("a"); pool.returnObject("a", s1); pool.returnObject("a", s2); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEviction() throws Exception { gkoPool.setMaxIdlePerKey(500); gkoPool.setMaxTotalPerKey(500); gkoPool.setNumTestsPerEvictionRun(100); gkoPool.setMinEvictableIdleTime(Duration.ofMillis(250)); gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); final String[] active = new String[500]; for(int i=0;i<500;i++) { active[i] = gkoPool.borrowObject(""); } for(int i=0;i<500;i++) { gkoPool.returnObject("",active[i]); } try { Thread.sleep(1000L); } catch(final InterruptedException e) { } assertTrue(gkoPool.getNumIdle("") < 500, "Should be less than 500 idle, found " + gkoPool.getNumIdle("")); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(gkoPool.getNumIdle("") < 400, "Should be less than 400 idle, found " + gkoPool.getNumIdle("")); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(gkoPool.getNumIdle("") < 300,"Should be less than 300 idle, found " + gkoPool.getNumIdle("")); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(gkoPool.getNumIdle("") < 200, "Should be less than 200 idle, found " + gkoPool.getNumIdle("")); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(gkoPool.getNumIdle("") < 100 , "Should be less than 100 idle, found " + gkoPool.getNumIdle("")); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertEquals(0,gkoPool.getNumIdle(""),"Should be zero idle, found " + gkoPool.getNumIdle("")); for(int i=0;i<500;i++) { active[i] = gkoPool.borrowObject(""); } for(int i=0;i<500;i++) { gkoPool.returnObject("",active[i]); } try { Thread.sleep(1000L); } catch(final InterruptedException e) { } assertTrue(gkoPool.getNumIdle("") < 500,"Should be less than 500 idle, found " + gkoPool.getNumIdle("")); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(gkoPool.getNumIdle("") < 400,"Should be less than 400 idle, found " + gkoPool.getNumIdle("")); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(gkoPool.getNumIdle("") < 300,"Should be less than 300 idle, found " + gkoPool.getNumIdle("")); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(gkoPool.getNumIdle("") < 200,"Should be less than 200 idle, found " + gkoPool.getNumIdle("")); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(gkoPool.getNumIdle("") < 100,"Should be less than 100 idle, found " + gkoPool.getNumIdle("")); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertEquals(0,gkoPool.getNumIdle(""),"Should be zero idle, found " + gkoPool.getNumIdle("")); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEviction2() throws Exception { gkoPool.setMaxIdlePerKey(500); gkoPool.setMaxTotalPerKey(500); gkoPool.setNumTestsPerEvictionRun(100); gkoPool.setMinEvictableIdleTime(Duration.ofMillis(500)); gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); final String[] active = new String[500]; final String[] active2 = new String[500]; for (int i = 0; i < 500; i++) { active[i] = gkoPool.borrowObject(""); active2[i] = gkoPool.borrowObject("2"); } for (int i = 0; i < 500; i++) { gkoPool.returnObject("", active[i]); gkoPool.returnObject("2", active2[i]); } try { Thread.sleep(1100L); } catch (final InterruptedException e) { // ignore } assertTrue(gkoPool.getNumIdle() < 1000, "Should be less than 1000 idle, found " + gkoPool.getNumIdle()); final long sleepMillisPart2 = 600L; try { Thread.sleep(sleepMillisPart2); } catch (final InterruptedException e) { // ignore } assertTrue(gkoPool.getNumIdle() < 900, "Should be less than 900 idle, found " + gkoPool.getNumIdle()); try { Thread.sleep(sleepMillisPart2); } catch (final InterruptedException e) { // ignore } assertTrue(gkoPool.getNumIdle() < 800, "Should be less than 800 idle, found " + gkoPool.getNumIdle()); try { Thread.sleep(sleepMillisPart2); } catch (final InterruptedException e) { // ignore } assertTrue(gkoPool.getNumIdle() < 700, "Should be less than 700 idle, found " + gkoPool.getNumIdle()); try { Thread.sleep(sleepMillisPart2); } catch (final InterruptedException e) { // ignore } assertTrue(gkoPool.getNumIdle() < 600, "Should be less than 600 idle, found " + gkoPool.getNumIdle()); try { Thread.sleep(sleepMillisPart2); } catch (final InterruptedException e) { // ignore } assertTrue(gkoPool.getNumIdle() < 500, "Should be less than 500 idle, found " + gkoPool.getNumIdle()); try { Thread.sleep(sleepMillisPart2); } catch (final InterruptedException e) { // ignore } assertTrue(gkoPool.getNumIdle() < 400, "Should be less than 400 idle, found " + gkoPool.getNumIdle()); try { Thread.sleep(sleepMillisPart2); } catch (final InterruptedException e) { // ignore } assertTrue(gkoPool.getNumIdle() < 300, "Should be less than 300 idle, found " + gkoPool.getNumIdle()); try { Thread.sleep(sleepMillisPart2); } catch (final InterruptedException e) { // ignore } assertTrue(gkoPool.getNumIdle() < 200, "Should be less than 200 idle, found " + gkoPool.getNumIdle()); try { Thread.sleep(sleepMillisPart2); } catch (final InterruptedException e) { // ignore } assertTrue(gkoPool.getNumIdle() < 100, "Should be less than 100 idle, found " + gkoPool.getNumIdle()); try { Thread.sleep(sleepMillisPart2); } catch (final InterruptedException e) { // ignore } assertEquals(0, gkoPool.getNumIdle(), "Should be zero idle, found " + gkoPool.getNumIdle()); } /** * Test to make sure evictor visits least recently used objects first, * regardless of FIFO/LIFO * * JIRA: POOL-86 * * @throws Exception May occur in some failure modes */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEvictionOrder() throws Exception { checkEvictionOrder(false); checkEvictionOrder(true); } // POOL-326 @Test public void testEvictorClearOldestRace() throws Exception { gkoPool.setMinEvictableIdleTime(Duration.ofMillis(100)); gkoPool.setNumTestsPerEvictionRun(1); // Introduce latency between when evictor starts looking at an instance and when // it decides to destroy it gkoPool.setEvictionPolicy(new SlowEvictionPolicy<>(1000)); // Borrow an instance final String val = gkoPool.borrowObject("foo"); // Add another idle one gkoPool.addObject("foo"); // Sleep long enough so idle one is eligible for eviction Thread.sleep(1000); // Start evictor and race with clearOldest gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(10)); // Wait for evictor to start Thread.sleep(100); gkoPool.clearOldest(); // Wait for slow evictor to complete Thread.sleep(1500); // See if we get NPE on return (POOL-326) gkoPool.returnObject("foo", val); } /** * Verifies that the evictor visits objects in expected order * and frequency. * * @throws Exception May occur in some failure modes */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEvictorVisiting() throws Exception { checkEvictorVisiting(true); checkEvictorVisiting(false); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testExceptionOnActivateDuringBorrow() throws Exception { final String obj1 = gkoPool.borrowObject("one"); final String obj2 = gkoPool.borrowObject("one"); gkoPool.returnObject("one", obj1); gkoPool.returnObject("one", obj2); simpleFactory.setThrowExceptionOnActivate(true); simpleFactory.setEvenValid(false); // Activation will now throw every other time // First attempt throws, but loop continues and second succeeds final String obj = gkoPool.borrowObject("one"); assertEquals(1, gkoPool.getNumActive("one")); assertEquals(0, gkoPool.getNumIdle("one")); assertEquals(1, gkoPool.getNumActive()); assertEquals(0, gkoPool.getNumIdle()); gkoPool.returnObject("one", obj); simpleFactory.setValid(false); // Validation will now fail on activation when borrowObject returns // an idle instance, and then when attempting to create a new instance try { gkoPool.borrowObject("one"); fail("Expecting NoSuchElementException"); } catch (final NoSuchElementException ex) { // expected } assertEquals(0, gkoPool.getNumActive("one")); assertEquals(0, gkoPool.getNumIdle("one")); assertEquals(0, gkoPool.getNumActive()); assertEquals(0, gkoPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testExceptionOnDestroyDuringBorrow() throws Exception { simpleFactory.setThrowExceptionOnDestroy(true); simpleFactory.setValidationEnabled(true); gkoPool.setTestOnBorrow(true); gkoPool.borrowObject("one"); simpleFactory.setValid(false); // Make validation fail on next borrow attempt try { gkoPool.borrowObject("one"); fail("Expecting NoSuchElementException"); } catch (final NoSuchElementException ex) { // expected } assertEquals(1, gkoPool.getNumActive("one")); assertEquals(0, gkoPool.getNumIdle("one")); assertEquals(1, gkoPool.getNumActive()); assertEquals(0, gkoPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testExceptionOnDestroyDuringReturn() throws Exception { simpleFactory.setThrowExceptionOnDestroy(true); simpleFactory.setValidationEnabled(true); gkoPool.setTestOnReturn(true); final String obj1 = gkoPool.borrowObject("one"); gkoPool.borrowObject("one"); simpleFactory.setValid(false); // Make validation fail gkoPool.returnObject("one", obj1); assertEquals(1, gkoPool.getNumActive("one")); assertEquals(0, gkoPool.getNumIdle("one")); assertEquals(1, gkoPool.getNumActive()); assertEquals(0, gkoPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testExceptionOnPassivateDuringReturn() throws Exception { final String obj = gkoPool.borrowObject("one"); simpleFactory.setThrowExceptionOnPassivate(true); gkoPool.returnObject("one", obj); assertEquals(0,gkoPool.getNumIdle()); gkoPool.close(); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testFIFO() throws Exception { gkoPool.setLifo(false); final String key = "key"; gkoPool.addObject(key); // "key0" gkoPool.addObject(key); // "key1" gkoPool.addObject(key); // "key2" assertEquals( "key0", gkoPool.borrowObject(key),"Oldest"); assertEquals( "key1", gkoPool.borrowObject(key),"Middle"); assertEquals( "key2", gkoPool.borrowObject(key),"Youngest"); final String s = gkoPool.borrowObject(key); assertEquals( "key3", s,"new-3"); gkoPool.returnObject(key, s); assertEquals( s, gkoPool.borrowObject(key),"returned"); assertEquals( "key4", gkoPool.borrowObject(key),"new-4"); } /** * Verify that threads waiting on a depleted pool get served when a checked out object is * invalidated. * * JIRA: POOL-240 * * @throws Exception May occur in some failure modes */ @Test public void testInvalidateFreesCapacity() throws Exception { final SimpleFactory factory = new SimpleFactory<>(); try (final GenericKeyedObjectPool pool = new GenericKeyedObjectPool<>(factory)) { pool.setMaxTotalPerKey(2); pool.setMaxWaitMillis(500); // Borrow an instance and hold if for 5 seconds final WaitingTestThread thread1 = new WaitingTestThread(pool, "one", 5000); thread1.start(); // Borrow another instance final String obj = pool.borrowObject("one"); // Launch another thread - will block, but fail in 500 ms final WaitingTestThread thread2 = new WaitingTestThread(pool, "one", 100); thread2.start(); // Invalidate the object borrowed by this thread - should allow thread2 to create Thread.sleep(20); pool.invalidateObject("one", obj); Thread.sleep(600); // Wait for thread2 to timeout if (thread2._thrown != null) { fail(thread2._thrown.toString()); } } } /** * Verify that threads blocked waiting on a depleted pool get served when a checked out instance * is invalidated. * * JIRA: POOL-240 * * @throws Exception May occur in some failure modes */ @Test public void testInvalidateWaiting() throws Exception { final GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig<>(); config.setMaxTotal(2); config.setBlockWhenExhausted(true); config.setMinIdlePerKey(0); config.setMaxWaitMillis(-1); config.setNumTestsPerEvictionRun(Integer.MAX_VALUE); // always test all idle objects config.setTestOnBorrow(true); config.setTestOnReturn(false); config.setTestWhileIdle(true); config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1)); try (final GenericKeyedObjectPool pool = new GenericKeyedObjectPool<>(new ObjectFactory(), config)) { // Allocate both objects with this thread pool.borrowObject(Integer.valueOf(1)); // object1 final Object object2 = pool.borrowObject(Integer.valueOf(1)); // Cause a thread to block waiting for an object final ExecutorService executorService = Executors.newSingleThreadExecutor(new DaemonThreadFactory()); final Semaphore signal = new Semaphore(0); final Future result = executorService.submit(() -> { try { signal.release(); final Object object3 = pool.borrowObject(Integer.valueOf(1)); pool.returnObject(Integer.valueOf(1), object3); signal.release(); } catch (final Exception e1) { return e1; } catch (final Throwable e2) { return new Exception(e2); } return null; }); // Wait for the thread to start assertTrue(signal.tryAcquire(5, TimeUnit.SECONDS)); // Attempt to ensure that test thread is blocked waiting for an object Thread.sleep(500); pool.invalidateObject(Integer.valueOf(1), object2); assertTrue(signal.tryAcquire(2, TimeUnit.SECONDS),"Call to invalidateObject did not unblock pool waiters."); if (result.get() != null) { throw new AssertionError(result.get()); } } } /** * Ensure the pool is registered. */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testJmxRegistration() { final ObjectName oname = gkoPool.getJmxName(); final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); final Set result = mbs.queryNames(oname, null); assertEquals(1, result.size()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testLIFO() throws Exception { gkoPool.setLifo(true); final String key = "key"; gkoPool.addObject(key); // "key0" gkoPool.addObject(key); // "key1" gkoPool.addObject(key); // "key2" assertEquals( "key2", gkoPool.borrowObject(key),"Youngest"); assertEquals( "key1", gkoPool.borrowObject(key),"Middle"); assertEquals( "key0", gkoPool.borrowObject(key),"Oldest"); final String s = gkoPool.borrowObject(key); assertEquals( "key3", s,"new-3"); gkoPool.returnObject(key, s); assertEquals( s, gkoPool.borrowObject(key),"returned"); assertEquals( "key4", gkoPool.borrowObject(key),"new-4"); } /** * Verifies that threads that get parked waiting for keys not in use * when the pool is at maxTotal eventually get served. * * @throws Exception May occur in some failure modes */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testLivenessPerKey() throws Exception { gkoPool.setMaxIdlePerKey(3); gkoPool.setMaxTotal(3); gkoPool.setMaxTotalPerKey(3); gkoPool.setMaxWaitMillis(3000); // Really a timeout for the test // Check out and briefly hold 3 "1"s final WaitingTestThread t1 = new WaitingTestThread(gkoPool, "1", 100); final WaitingTestThread t2 = new WaitingTestThread(gkoPool, "1", 100); final WaitingTestThread t3 = new WaitingTestThread(gkoPool, "1", 100); t1.start(); t2.start(); t3.start(); // Try to get a "2" while all capacity is in use. // Thread will park waiting on empty queue. Verify it gets served. gkoPool.borrowObject("2"); } /** * Verify that factory exceptions creating objects do not corrupt per key create count. * * JIRA: POOL-243 * * @throws Exception May occur in some failure modes */ @Test public void testMakeObjectException() throws Exception { final SimpleFactory factory = new SimpleFactory<>(); try (final GenericKeyedObjectPool pool = new GenericKeyedObjectPool<>(factory)) { pool.setMaxTotalPerKey(1); pool.setBlockWhenExhausted(false); factory.exceptionOnCreate = true; try { pool.borrowObject("One"); } catch (final Exception ex) { // expected } factory.exceptionOnCreate = false; pool.borrowObject("One"); } } /** * Test case for POOL-180. * * @throws Exception May occur in some failure modes */ @Test @Timeout(value = 200000, unit = TimeUnit.MILLISECONDS) public void testMaxActivePerKeyExceeded() throws Exception { final WaiterFactory waiterFactory = new WaiterFactory<>(0, 20, 0, 0, 0, 0, 8, 5, 0); // TODO Fix this. Can't use local pool since runTestThreads uses the // protected pool field try (final GenericKeyedObjectPool waiterPool = new GenericKeyedObjectPool<>(waiterFactory)) { waiterPool.setMaxTotalPerKey(5); waiterPool.setMaxTotal(8); waiterPool.setTestOnBorrow(true); waiterPool.setMaxIdlePerKey(5); waiterPool.setMaxWaitMillis(-1); runTestThreads(20, 300, 250, waiterPool); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxIdle() throws Exception { gkoPool.setMaxTotalPerKey(100); gkoPool.setMaxIdlePerKey(8); final String[] active = new String[100]; for(int i=0;i<100;i++) { active[i] = gkoPool.borrowObject(""); } assertEquals(100,gkoPool.getNumActive("")); assertEquals(0,gkoPool.getNumIdle("")); for(int i=0;i<100;i++) { gkoPool.returnObject("",active[i]); assertEquals(99 - i,gkoPool.getNumActive("")); assertEquals((i < 8 ? i+1 : 8),gkoPool.getNumIdle("")); } for(int i=0;i<100;i++) { active[i] = gkoPool.borrowObject("a"); } assertEquals(100,gkoPool.getNumActive("a")); assertEquals(0,gkoPool.getNumIdle("a")); for(int i=0;i<100;i++) { gkoPool.returnObject("a",active[i]); assertEquals(99 - i,gkoPool.getNumActive("a")); assertEquals((i < 8 ? i+1 : 8),gkoPool.getNumIdle("a")); } // total number of idle instances is twice maxIdle assertEquals(16, gkoPool.getNumIdle()); // Each pool is at the sup assertEquals(8, gkoPool.getNumIdle("")); assertEquals(8, gkoPool.getNumIdle("a")); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxTotal() throws Exception { gkoPool.setMaxTotalPerKey(2); gkoPool.setMaxTotal(3); gkoPool.setBlockWhenExhausted(false); final String o1 = gkoPool.borrowObject("a"); assertNotNull(o1); final String o2 = gkoPool.borrowObject("a"); assertNotNull(o2); final String o3 = gkoPool.borrowObject("b"); assertNotNull(o3); try { gkoPool.borrowObject("c"); fail("Expected NoSuchElementException"); } catch(final NoSuchElementException e) { // expected } assertEquals(0, gkoPool.getNumIdle()); gkoPool.returnObject("b", o3); assertEquals(1, gkoPool.getNumIdle()); assertEquals(1, gkoPool.getNumIdle("b")); final Object o4 = gkoPool.borrowObject("b"); assertNotNull(o4); assertEquals(0, gkoPool.getNumIdle()); assertEquals(0, gkoPool.getNumIdle("b")); gkoPool.setMaxTotal(4); final Object o5 = gkoPool.borrowObject("b"); assertNotNull(o5); assertEquals(2, gkoPool.getNumActive("a")); assertEquals(2, gkoPool.getNumActive("b")); assertEquals(gkoPool.getMaxTotal(), gkoPool.getNumActive("b") + gkoPool.getNumActive("b")); assertEquals(gkoPool.getNumActive(), gkoPool.getMaxTotal()); } /** * Verifies that maxTotal is not exceeded when factory destroyObject * has high latency, testOnReturn is set and there is high incidence of * validation failures. * * @throws Exception May occur in some failure modes */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxTotalInvariant() throws Exception { final int maxTotal = 15; simpleFactory.setEvenValid(false); // Every other validation fails simpleFactory.setDestroyLatency(100); // Destroy takes 100 ms simpleFactory.setMaxTotalPerKey(maxTotal); // (makes - destroys) bound simpleFactory.setValidationEnabled(true); gkoPool.setMaxTotal(maxTotal); gkoPool.setMaxIdlePerKey(-1); gkoPool.setTestOnReturn(true); gkoPool.setMaxWaitMillis(10000L); runTestThreads(5, 10, 50, gkoPool); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxTotalLRU() throws Exception { gkoPool.setMaxTotalPerKey(2); gkoPool.setMaxTotal(3); final String o1 = gkoPool.borrowObject("a"); assertNotNull(o1); gkoPool.returnObject("a", o1); Thread.sleep(25); final String o2 = gkoPool.borrowObject("b"); assertNotNull(o2); gkoPool.returnObject("b", o2); Thread.sleep(25); final String o3 = gkoPool.borrowObject("c"); assertNotNull(o3); gkoPool.returnObject("c", o3); Thread.sleep(25); final String o4 = gkoPool.borrowObject("a"); assertNotNull(o4); gkoPool.returnObject("a", o4); Thread.sleep(25); assertSame(o1, o4); // this should cause b to be bumped out of the pool final String o5 = gkoPool.borrowObject("d"); assertNotNull(o5); gkoPool.returnObject("d", o5); Thread.sleep(25); // now re-request b, we should get a different object because it should // have been expelled from pool (was oldest because a was requested after b) final String o6 = gkoPool.borrowObject("b"); assertNotNull(o6); gkoPool.returnObject("b", o6); assertNotSame(o1, o6); assertNotSame(o2, o6); // second a is still in there final String o7 = gkoPool.borrowObject("a"); assertNotNull(o7); gkoPool.returnObject("a", o7); assertSame(o4, o7); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxTotalPerKey() throws Exception { gkoPool.setMaxTotalPerKey(3); gkoPool.setBlockWhenExhausted(false); gkoPool.borrowObject(""); gkoPool.borrowObject(""); gkoPool.borrowObject(""); try { gkoPool.borrowObject(""); fail("Expected NoSuchElementException"); } catch(final NoSuchElementException e) { // expected } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxTotalPerKeyZero() throws Exception { gkoPool.setMaxTotalPerKey(0); gkoPool.setBlockWhenExhausted(false); try { gkoPool.borrowObject("a"); fail("Expected NoSuchElementException"); } catch(final NoSuchElementException e) { // expected } } /** * Verifies that if a borrow of a new key is blocked because maxTotal has * been reached, that borrow continues once another object is returned. * * JIRA: POOL-310 */ @Test public void testMaxTotalWithThreads() throws Exception { gkoPool.setMaxTotalPerKey(2); gkoPool.setMaxTotal(1); final int holdTime = 2000; final TestThread testA = new TestThread<>(gkoPool, 1, 0, holdTime, false, null, "a"); final TestThread testB = new TestThread<>(gkoPool, 1, 0, holdTime, false, null, "b"); final Thread threadA = new Thread(testA); final Thread threadB = new Thread(testB); threadA.start(); threadB.start(); Thread.sleep(holdTime * 2); // Both threads should be complete now. boolean threadRunning = true; int count = 0; while (threadRunning && count < 15) { threadRunning = threadA.isAlive(); threadRunning = threadB.isAlive(); Thread.sleep(200); count++; } assertFalse(threadA.isAlive()); assertFalse(threadB.isAlive()); assertFalse(testA._failed); assertFalse(testB._failed); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxTotalZero() throws Exception { gkoPool.setMaxTotal(0); gkoPool.setBlockWhenExhausted(false); try { gkoPool.borrowObject("a"); fail("Expected NoSuchElementException"); } catch(final NoSuchElementException e) { // expected } } /* * Test multi-threaded pool access. * Multiple keys, multiple threads, but maxActive only allows half the threads to succeed. * * This test was prompted by Continuum build failures in the Commons DBCP test case: * TestSharedPoolDataSource.testMultipleThreads2() * Let's see if the this fails on Continuum too! */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxWaitMultiThreaded() throws Exception { final long maxWait = 500; // wait for connection final long holdTime = 4 * maxWait; // how long to hold connection final int keyCount = 4; // number of different keys final int threadsPerKey = 5; // number of threads to grab the key initially gkoPool.setBlockWhenExhausted(true); gkoPool.setMaxWaitMillis(maxWait); gkoPool.setMaxTotalPerKey(threadsPerKey); // Create enough threads so half the threads will have to wait final WaitingTestThread[] wtt = new WaitingTestThread[keyCount * threadsPerKey * 2]; for(int i=0; i < wtt.length; i++){ wtt[i] = new WaitingTestThread(gkoPool,Integer.toString(i % keyCount),holdTime); } final long originMillis = System.currentTimeMillis() - 1000; for (final WaitingTestThread element : wtt) { element.start(); } int failed = 0; for (final WaitingTestThread element : wtt) { element.join(); if (element._thrown != null){ failed++; } } if (DISPLAY_THREAD_DETAILS || wtt.length/2 != failed){ System.out.println( "MaxWait: " + maxWait + " HoldTime: " + holdTime + " KeyCount: " + keyCount + " MaxActive: " + threadsPerKey + " Threads: " + wtt.length + " Failed: " + failed ); for (final WaitingTestThread wt : wtt) { System.out.println( "Preborrow: " + (wt.preBorrowMillis - originMillis) + " Postborrow: " + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - originMillis : -1) + " BorrowTime: " + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - wt.preBorrowMillis : -1) + " PostReturn: " + (wt.postReturnMillis != 0 ? wt.postReturnMillis - originMillis : -1) + " Ended: " + (wt.endedMillis - originMillis) + " Key: " + (wt._key) + " ObjId: " + wt.objectId ); } } assertEquals(wtt.length/2,failed,"Expected half the threads to fail"); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMinIdle() throws Exception { gkoPool.setMaxIdlePerKey(500); gkoPool.setMinIdlePerKey(5); gkoPool.setMaxTotalPerKey(10); gkoPool.setNumTestsPerEvictionRun(0); gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50)); gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); gkoPool.setTestWhileIdle(true); //Generate a random key final String key = "A"; gkoPool.preparePool(key); try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle()); final String[] active = new String[5]; active[0] = gkoPool.borrowObject(key); try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle()); for(int i=1 ; i<5 ; i++) { active[i] = gkoPool.borrowObject(key); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle()); for(int i=0 ; i<5 ; i++) { gkoPool.returnObject(key, active[i]); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(10, gkoPool.getNumIdle(), "Should be 10 idle, found " + gkoPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMinIdleMaxTotalPerKey() throws Exception { gkoPool.setMaxIdlePerKey(500); gkoPool.setMinIdlePerKey(5); gkoPool.setMaxTotalPerKey(10); gkoPool.setNumTestsPerEvictionRun(0); gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50)); gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); gkoPool.setTestWhileIdle(true); final String key = "A"; gkoPool.preparePool(key); assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle()); try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle()); final String[] active = new String[10]; try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle()); for(int i=0 ; i<5 ; i++) { active[i] = gkoPool.borrowObject(key); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle()); for(int i=0 ; i<5 ; i++) { gkoPool.returnObject(key, active[i]); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(10, gkoPool.getNumIdle(), "Should be 10 idle, found " + gkoPool.getNumIdle()); for(int i=0 ; i<10 ; i++) { active[i] = gkoPool.borrowObject(key); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(0, gkoPool.getNumIdle(), "Should be 0 idle, found " + gkoPool.getNumIdle()); for(int i=0 ; i<10 ; i++) { gkoPool.returnObject(key, active[i]); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(10, gkoPool.getNumIdle(), "Should be 10 idle, found " + gkoPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMinIdleNoPreparePool() throws Exception { gkoPool.setMaxIdlePerKey(500); gkoPool.setMinIdlePerKey(5); gkoPool.setMaxTotalPerKey(10); gkoPool.setNumTestsPerEvictionRun(0); gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50)); gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); gkoPool.setTestWhileIdle(true); //Generate a random key final String key = "A"; try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(0, gkoPool.getNumIdle(), "Should be 0 idle, found " + gkoPool.getNumIdle()); final Object active = gkoPool.borrowObject(key); assertNotNull(active); try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle()); } /** * Verifies that returning an object twice (without borrow in between) causes ISE * but does not re-validate or re-passivate the instance. * * JIRA: POOL-285 */ @Test public void testMultipleReturn() throws Exception { final WaiterFactory factory = new WaiterFactory<>(0, 0, 0, 0, 0, 0); try (final GenericKeyedObjectPool pool = new GenericKeyedObjectPool<>(factory)) { pool.setTestOnReturn(true); final Waiter waiter = pool.borrowObject("a"); pool.returnObject("a", waiter); assertEquals(1, waiter.getValidationCount()); assertEquals(1, waiter.getPassivationCount()); try { pool.returnObject("a", waiter); fail("Expecting IllegalStateException from multiple return"); } catch (final IllegalStateException ex) { // Exception is expected, now check no repeat validation/passivation assertEquals(1, waiter.getValidationCount()); assertEquals(1, waiter.getPassivationCount()); } } } /** * Verifies that when a borrowed object is mutated in a way that does not * preserve equality and hashcode, the pool can recognized it on return. * * JIRA: POOL-284 */ @Test public void testMutable() throws Exception { final HashSetFactory factory = new HashSetFactory(); try (final GenericKeyedObjectPool> pool = new GenericKeyedObjectPool<>(factory, new GenericKeyedObjectPoolConfig<>())) { final HashSet s1 = pool.borrowObject("a"); final HashSet s2 = pool.borrowObject("a"); s1.add("One"); s2.add("One"); pool.returnObject("a", s1); pool.returnObject("a", s2); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testNegativeMaxTotalPerKey() throws Exception { gkoPool.setMaxTotalPerKey(-1); gkoPool.setBlockWhenExhausted(false); final String obj = gkoPool.borrowObject(""); assertEquals("0",obj); gkoPool.returnObject("",obj); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testNumActiveNumIdle2() throws Exception { assertEquals(0,gkoPool.getNumActive()); assertEquals(0,gkoPool.getNumIdle()); assertEquals(0,gkoPool.getNumActive("A")); assertEquals(0,gkoPool.getNumIdle("A")); assertEquals(0,gkoPool.getNumActive("B")); assertEquals(0,gkoPool.getNumIdle("B")); final String objA0 = gkoPool.borrowObject("A"); final String objB0 = gkoPool.borrowObject("B"); assertEquals(2,gkoPool.getNumActive()); assertEquals(0,gkoPool.getNumIdle()); assertEquals(1,gkoPool.getNumActive("A")); assertEquals(0,gkoPool.getNumIdle("A")); assertEquals(1,gkoPool.getNumActive("B")); assertEquals(0,gkoPool.getNumIdle("B")); final String objA1 = gkoPool.borrowObject("A"); final String objB1 = gkoPool.borrowObject("B"); assertEquals(4,gkoPool.getNumActive()); assertEquals(0,gkoPool.getNumIdle()); assertEquals(2,gkoPool.getNumActive("A")); assertEquals(0,gkoPool.getNumIdle("A")); assertEquals(2,gkoPool.getNumActive("B")); assertEquals(0,gkoPool.getNumIdle("B")); gkoPool.returnObject("A",objA0); gkoPool.returnObject("B",objB0); assertEquals(2,gkoPool.getNumActive()); assertEquals(2,gkoPool.getNumIdle()); assertEquals(1,gkoPool.getNumActive("A")); assertEquals(1,gkoPool.getNumIdle("A")); assertEquals(1,gkoPool.getNumActive("B")); assertEquals(1,gkoPool.getNumIdle("B")); gkoPool.returnObject("A",objA1); gkoPool.returnObject("B",objB1); assertEquals(0,gkoPool.getNumActive()); assertEquals(4,gkoPool.getNumIdle()); assertEquals(0,gkoPool.getNumActive("A")); assertEquals(2,gkoPool.getNumIdle("A")); assertEquals(0,gkoPool.getNumActive("B")); assertEquals(2,gkoPool.getNumIdle("B")); } @Test public void testReturnObjectThrowsIllegalStateException() { try (final GenericKeyedObjectPool pool = new GenericKeyedObjectPool<>(new SimpleFactory<>())) { assertThrows(IllegalStateException.class, () -> pool.returnObject("Foo", "Bar")); } } /** * JIRA: POOL-287 * * Verify that when an attempt is made to borrow an instance from the pool * while the evictor is visiting it, there is no capacity leak. * * Test creates the scenario described in POOL-287. */ @Test public void testReturnToHead() throws Exception { final SimpleFactory factory = new SimpleFactory<>(); factory.setValidateLatency(100); factory.setValid(true); // Validation always succeeds try (final GenericKeyedObjectPool pool = new GenericKeyedObjectPool<>(factory)) { pool.setMaxWaitMillis(1000); pool.setTestWhileIdle(true); pool.setMaxTotalPerKey(2); pool.setNumTestsPerEvictionRun(1); pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); // Load pool with two objects pool.addObject("one"); // call this o1 pool.addObject("one"); // call this o2 // Default is LIFO, so "one" pool is now [o2, o1] in offer order. // Evictor will visit in oldest-to-youngest order, so o1 then o2 Thread.sleep(800); // Wait for first eviction run to complete // At this point, one eviction run should have completed, visiting o1 // and eviction cursor should be pointed at o2, which is the next offered instance Thread.sleep(250); // Wait for evictor to start final String o1 = pool.borrowObject("one"); // o2 is under eviction, so this will return o1 final String o2 = pool.borrowObject("one"); // Once validation completes, o2 should be offered pool.returnObject("one", o1); pool.returnObject("one", o2); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testSettersAndGetters() throws Exception { { gkoPool.setMaxTotalPerKey(123); assertEquals(123, gkoPool.getMaxTotalPerKey()); } { gkoPool.setMaxIdlePerKey(12); assertEquals(12, gkoPool.getMaxIdlePerKey()); } { gkoPool.setMaxWaitMillis(1234L); assertEquals(1234L, gkoPool.getMaxWaitMillis()); } { gkoPool.setMinEvictableIdleTimeMillis(12345L); assertEquals(12345L, gkoPool.getMinEvictableIdleTimeMillis()); } { gkoPool.setMinEvictableIdleTime(Duration.ofMillis(12345L)); assertEquals(12345L, gkoPool.getMinEvictableIdleTime().toMillis()); } { gkoPool.setNumTestsPerEvictionRun(11); assertEquals(11, gkoPool.getNumTestsPerEvictionRun()); } { gkoPool.setTestOnBorrow(true); assertTrue(gkoPool.getTestOnBorrow()); gkoPool.setTestOnBorrow(false); assertFalse(gkoPool.getTestOnBorrow()); } { gkoPool.setTestOnReturn(true); assertTrue(gkoPool.getTestOnReturn()); gkoPool.setTestOnReturn(false); assertFalse(gkoPool.getTestOnReturn()); } { gkoPool.setTestWhileIdle(true); assertTrue(gkoPool.getTestWhileIdle()); gkoPool.setTestWhileIdle(false); assertFalse(gkoPool.getTestWhileIdle()); } { gkoPool.setTimeBetweenEvictionRunsMillis(11235L); assertEquals(11235L, gkoPool.getTimeBetweenEvictionRunsMillis()); } { gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(11235L)); assertEquals(11235L, gkoPool.getTimeBetweenEvictionRuns().toMillis()); } { gkoPool.setBlockWhenExhausted(true); assertTrue(gkoPool.getBlockWhenExhausted()); gkoPool.setBlockWhenExhausted(false); assertFalse(gkoPool.getBlockWhenExhausted()); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testThreaded1() throws Exception { gkoPool.setMaxTotalPerKey(15); gkoPool.setMaxIdlePerKey(15); gkoPool.setMaxWaitMillis(1000L); runTestThreads(20, 100, 50, gkoPool); } // Pool-361 @Test public void testValidateOnCreate() throws Exception { gkoPool.setTestOnCreate(true); simpleFactory.setValidationEnabled(true); gkoPool.addObject("one"); assertEquals(1, simpleFactory.validateCounter); } @Test public void testValidateOnCreateFailure() throws Exception { gkoPool.setTestOnCreate(true); gkoPool.setTestOnBorrow(false); gkoPool.setMaxTotal(2); simpleFactory.setValidationEnabled(true); simpleFactory.setValid(false); // Make sure failed validations do not leak capacity gkoPool.addObject("one"); gkoPool.addObject("one"); assertEquals(0, gkoPool.getNumIdle()); assertEquals(0, gkoPool.getNumActive()); simpleFactory.setValid(true); final String obj = gkoPool.borrowObject("one"); assertNotNull(obj); gkoPool.addObject("one"); // Should have one idle, one out now assertEquals(1, gkoPool.getNumIdle()); assertEquals(1, gkoPool.getNumActive()); } /** * Verify that threads waiting on a depleted pool get served when a returning object fails * validation. * * JIRA: POOL-240 * * @throws Exception May occur in some failure modes */ @Test public void testValidationFailureOnReturnFreesCapacity() throws Exception { final SimpleFactory factory = new SimpleFactory<>(); factory.setValid(false); // Validate will always fail factory.setValidationEnabled(true); try (final GenericKeyedObjectPool pool = new GenericKeyedObjectPool<>(factory)) { pool.setMaxTotalPerKey(2); pool.setMaxWaitMillis(1500); pool.setTestOnReturn(true); pool.setTestOnBorrow(false); // Borrow an instance and hold if for 5 seconds final WaitingTestThread thread1 = new WaitingTestThread(pool, "one", 5000); thread1.start(); // Borrow another instance and return it after 500 ms (validation will fail) final WaitingTestThread thread2 = new WaitingTestThread(pool, "one", 500); thread2.start(); Thread.sleep(50); // Try to borrow an object final String obj = pool.borrowObject("one"); pool.returnObject("one", obj); } } // POOL-276 @Test public void testValidationOnCreateOnly() throws Exception { simpleFactory.enableValidation = true; gkoPool.setMaxTotal(1); gkoPool.setTestOnCreate(true); gkoPool.setTestOnBorrow(false); gkoPool.setTestOnReturn(false); gkoPool.setTestWhileIdle(false); final String o1 = gkoPool.borrowObject("KEY"); assertEquals("KEY0", o1); final Timer t = new Timer(); t.schedule( new TimerTask() { @Override public void run() { gkoPool.returnObject("KEY", o1); } }, 3000); final String o2 = gkoPool.borrowObject("KEY"); assertEquals("KEY0", o2); assertEquals(1, simpleFactory.validateCounter); } /** * POOL-189 * * @throws Exception May occur in some failure modes */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testWhenExhaustedBlockClosePool() throws Exception { gkoPool.setMaxTotalPerKey(1); gkoPool.setBlockWhenExhausted(true); gkoPool.setMaxWaitMillis(-1); final String obj1 = gkoPool.borrowObject("a"); // Make sure an object was obtained assertNotNull(obj1); // Create a separate thread to try and borrow another object final WaitingTestThread wtt = new WaitingTestThread(gkoPool, "a", 200); wtt.start(); // Give wtt time to start Thread.sleep(200); // close the pool (Bug POOL-189) gkoPool.close(); // Give interrupt time to take effect Thread.sleep(200); // Check thread was interrupted assertTrue(wtt._thrown instanceof InterruptedException); } } TestGenericObjectPool.java000066400000000000000000003456551405425132200345630ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.lang.management.ManagementFactory; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.nio.charset.UnsupportedCharsetException; import java.time.Duration; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import javax.management.MBeanServer; import javax.management.ObjectName; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.PoolUtils; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.SwallowedExceptionListener; import org.apache.commons.pool2.TestBaseObjectPool; import org.apache.commons.pool2.VisitTracker; import org.apache.commons.pool2.VisitTrackerFactory; import org.apache.commons.pool2.Waiter; import org.apache.commons.pool2.WaiterFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; /** */ public class TestGenericObjectPool extends TestBaseObjectPool { private class ConcurrentBorrowAndEvictThread extends Thread { private final boolean borrow; public String obj; public ConcurrentBorrowAndEvictThread(final boolean borrow) { this.borrow = borrow; } @Override public void run() { try { if (borrow) { obj = genericObjectPool.borrowObject(); } else { genericObjectPool.evict(); } } catch (final Exception e) { /* Ignore */} } } private static class CreateErrorFactory extends BasePooledObjectFactory { private final Semaphore semaphore = new Semaphore(0); @Override public String create() throws Exception { semaphore.acquire(); throw new UnknownError("wiggle"); } public boolean hasQueuedThreads() { return semaphore.hasQueuedThreads(); } public void release() { semaphore.release(); } @Override public PooledObject wrap(final String obj) { return new DefaultPooledObject<>(obj); } } private static class CreateFailFactory extends BasePooledObjectFactory { private final Semaphore semaphore = new Semaphore(0); @Override public String create() throws Exception { semaphore.acquire(); throw new UnsupportedCharsetException("wibble"); } public boolean hasQueuedThreads() { return semaphore.hasQueuedThreads(); } public void release() { semaphore.release(); } @Override public PooledObject wrap(final String obj) { return new DefaultPooledObject<>(obj); } } private static final class DummyFactory extends BasePooledObjectFactory { @Override public Object create() throws Exception { return null; } @Override public PooledObject wrap(final Object value) { return new DefaultPooledObject<>(value); } } private static class EvictionThread extends Thread { private final GenericObjectPool pool; public EvictionThread(final GenericObjectPool pool) { this.pool = pool; } @Override public void run() { try { pool.evict(); } catch (final Exception e) { // Ignore } } } /** * Factory that creates HashSets. Note that this means * 0) All instances are initially equal (not discernible by equals) * 1) Instances are mutable and mutation can cause change in identity / hashcode. */ private static final class HashSetFactory extends BasePooledObjectFactory> { @Override public HashSet create() throws Exception { return new HashSet<>(); } @Override public PooledObject> wrap(final HashSet value) { return new DefaultPooledObject<>(value); } } /** * Attempts to invalidate an object, swallowing IllegalStateException. */ static class InvalidateThread implements Runnable { private final String obj; private final ObjectPool pool; private boolean done; public InvalidateThread(final ObjectPool pool, final String obj) { this.obj = obj; this.pool = pool; } public boolean complete() { return done; } @Override public void run() { try { pool.invalidateObject(obj); } catch (final IllegalStateException ex) { // Ignore } catch (final Exception ex) { fail("Unexpected exception " + ex.toString()); } finally { done = true; } } } private static class InvalidFactory extends BasePooledObjectFactory { @Override public Object create() throws Exception { return new Object(); } @Override public boolean validateObject(final PooledObject obj) { try { Thread.sleep(1000); } catch (final InterruptedException e) { // Ignore } return false; } @Override public PooledObject wrap(final Object value) { return new DefaultPooledObject<>(value); } } public static class SimpleFactory implements PooledObjectFactory { int makeCounter; int activationCounter; int validateCounter; int activeCount; boolean evenValid = true; boolean oddValid = true; boolean exceptionOnPassivate; boolean exceptionOnActivate; boolean exceptionOnDestroy; boolean enableValidation = true; long destroyLatency; long makeLatency; long validateLatency; int maxTotal = Integer.MAX_VALUE; public SimpleFactory() { this(true); } public SimpleFactory(final boolean valid) { this(valid,valid); } public SimpleFactory(final boolean evalid, final boolean ovalid) { evenValid = evalid; oddValid = ovalid; } @Override public void activateObject(final PooledObject obj) throws Exception { final boolean hurl; final boolean evenTest; final boolean oddTest; final int counter; synchronized(this) { hurl = exceptionOnActivate; evenTest = evenValid; oddTest = oddValid; counter = activationCounter++; } if (hurl && !(counter%2 == 0 ? evenTest : oddTest)) { throw new Exception(); } } @Override public void destroyObject(final PooledObject obj) throws Exception { final long waitLatency; final boolean hurl; synchronized(this) { waitLatency = destroyLatency; hurl = exceptionOnDestroy; } if (waitLatency > 0) { doWait(waitLatency); } synchronized(this) { activeCount--; } if (hurl) { throw new Exception(); } } private void doWait(final long latency) { try { Thread.sleep(latency); } catch (final InterruptedException ex) { // ignore } } public synchronized int getMakeCounter() { return makeCounter; } public synchronized boolean isThrowExceptionOnActivate() { return exceptionOnActivate; } public synchronized boolean isValidationEnabled() { return enableValidation; } @Override public PooledObject makeObject() { final long waitLatency; synchronized(this) { activeCount++; if (activeCount > maxTotal) { throw new IllegalStateException( "Too many active instances: " + activeCount); } waitLatency = makeLatency; } if (waitLatency > 0) { doWait(waitLatency); } final int counter; synchronized(this) { counter = makeCounter++; } return new DefaultPooledObject<>(String.valueOf(counter)); } @Override public void passivateObject(final PooledObject obj) throws Exception { final boolean hurl; synchronized(this) { hurl = exceptionOnPassivate; } if (hurl) { throw new Exception(); } } public synchronized void setDestroyLatency(final long destroyLatency) { this.destroyLatency = destroyLatency; } public synchronized void setEvenValid(final boolean valid) { evenValid = valid; } public synchronized void setMakeLatency(final long makeLatency) { this.makeLatency = makeLatency; } public synchronized void setMaxTotal(final int maxTotal) { this.maxTotal = maxTotal; } public synchronized void setOddValid(final boolean valid) { oddValid = valid; } public synchronized void setThrowExceptionOnActivate(final boolean b) { exceptionOnActivate = b; } public synchronized void setThrowExceptionOnDestroy(final boolean b) { exceptionOnDestroy = b; } public synchronized void setThrowExceptionOnPassivate(final boolean bool) { exceptionOnPassivate = bool; } public synchronized void setValid(final boolean valid) { setEvenValid(valid); setOddValid(valid); } public synchronized void setValidateLatency(final long validateLatency) { this.validateLatency = validateLatency; } public synchronized void setValidationEnabled(final boolean b) { enableValidation = b; } @Override public boolean validateObject(final PooledObject obj) { final boolean validate; final boolean evenTest; final boolean oddTest; final long waitLatency; final int counter; synchronized(this) { validate = enableValidation; evenTest = evenValid; oddTest = oddValid; counter = validateCounter++; waitLatency = validateLatency; } if (waitLatency > 0) { doWait(waitLatency); } if (validate) { return counter%2 == 0 ? evenTest : oddTest; } return true; } } public static class TestEvictionPolicy implements EvictionPolicy { private final AtomicInteger callCount = new AtomicInteger(0); @Override public boolean evict(final EvictionConfig config, final PooledObject underTest, final int idleCount) { return callCount.incrementAndGet() > 1500; } } static class TestThread implements Runnable { /** source of random delay times */ private final java.util.Random _random; /** pool to borrow from */ private final ObjectPool _pool; /** number of borrow attempts */ private final int _iter; /** delay before each borrow attempt */ private final int _startDelay; /** time to hold each borrowed object before returning it */ private final int _holdTime; /** whether or not start and hold time are randomly generated */ private final boolean _randomDelay; /** object expected to be borrowed (fail otherwise) */ private final Object _expectedObject; private volatile boolean _complete; private volatile boolean _failed; private volatile Throwable _error; public TestThread(final ObjectPool pool) { this(pool, 100, 50, true, null); } public TestThread(final ObjectPool pool, final int iter) { this(pool, iter, 50, true, null); } public TestThread(final ObjectPool pool, final int iter, final int delay) { this(pool, iter, delay, true, null); } public TestThread(final ObjectPool pool, final int iter, final int delay, final boolean randomDelay) { this(pool, iter, delay, randomDelay, null); } public TestThread(final ObjectPool pool, final int iter, final int delay, final boolean randomDelay, final Object obj) { this(pool, iter, delay, delay, randomDelay, obj); } public TestThread(final ObjectPool pool, final int iter, final int startDelay, final int holdTime, final boolean randomDelay, final Object obj) { _pool = pool; _iter = iter; _startDelay = startDelay; _holdTime = holdTime; _randomDelay = randomDelay; _random = _randomDelay ? new Random() : null; _expectedObject = obj; } public boolean complete() { return _complete; } public boolean failed() { return _failed; } @Override public void run() { for(int i=0;i<_iter;i++) { final long startDelay = _randomDelay ? (long)_random.nextInt(_startDelay) : _startDelay; final long holdTime = _randomDelay ? (long)_random.nextInt(_holdTime) : _holdTime; try { Thread.sleep(startDelay); } catch(final InterruptedException e) { // ignored } T obj = null; try { obj = _pool.borrowObject(); } catch(final Exception e) { _error = e; _failed = true; _complete = true; break; } if (_expectedObject != null && !_expectedObject.equals(obj)) { _error = new Throwable("Expected: "+_expectedObject+ " found: "+obj); _failed = true; _complete = true; break; } try { Thread.sleep(holdTime); } catch(final InterruptedException e) { // ignored } try { _pool.returnObject(obj); } catch(final Exception e) { _error = e; _failed = true; _complete = true; break; } } _complete = true; } } /* * Very simple test thread that just tries to borrow an object from * the provided pool returns it after a wait */ static class WaitingTestThread extends Thread { private final GenericObjectPool _pool; private final long _pause; private Throwable _thrown; private long preBorrowMillis; // just before borrow private long postBorrowMillis; // borrow returned private long postReturnMillis; // after object was returned private long endedMillis; private String objectId; public WaitingTestThread(final GenericObjectPool pool, final long pause) { _pool = pool; _pause = pause; _thrown = null; } @Override public void run() { try { preBorrowMillis = System.currentTimeMillis(); final String obj = _pool.borrowObject(); objectId = obj; postBorrowMillis = System.currentTimeMillis(); Thread.sleep(_pause); _pool.returnObject(obj); postReturnMillis = System.currentTimeMillis(); } catch (final Throwable e) { _thrown = e; } finally{ endedMillis = System.currentTimeMillis(); } } } private static final boolean DISPLAY_THREAD_DETAILS= Boolean.parseBoolean(System.getProperty("TestGenericObjectPool.display.thread.details", "false")); // To pass this to a Maven test, use: // mvn test -DargLine="-DTestGenericObjectPool.display.thread.details=true" // @see https://issues.apache.org/jira/browse/SUREFIRE-121 protected GenericObjectPool genericObjectPool; private SimpleFactory simpleFactory; @SuppressWarnings("deprecation") private void assertConfiguration(final GenericObjectPoolConfig expected, final GenericObjectPool actual) throws Exception { assertEquals(Boolean.valueOf(expected.getTestOnCreate()), Boolean.valueOf(actual.getTestOnCreate()), "testOnCreate"); assertEquals(Boolean.valueOf(expected.getTestOnBorrow()), Boolean.valueOf(actual.getTestOnBorrow()), "testOnBorrow"); assertEquals(Boolean.valueOf(expected.getTestOnReturn()), Boolean.valueOf(actual.getTestOnReturn()), "testOnReturn"); assertEquals(Boolean.valueOf(expected.getTestWhileIdle()), Boolean.valueOf(actual.getTestWhileIdle()), "testWhileIdle"); assertEquals(Boolean.valueOf(expected.getBlockWhenExhausted()), Boolean.valueOf(actual.getBlockWhenExhausted()), "whenExhaustedAction"); assertEquals(expected.getMaxTotal(), actual.getMaxTotal(), "maxTotal"); assertEquals(expected.getMaxIdle(), actual.getMaxIdle(), "maxIdle"); assertEquals(expected.getMaxWaitMillis(), actual.getMaxWaitMillis(), "maxWait"); assertEquals(expected.getMinEvictableIdleTimeMillis(), actual.getMinEvictableIdleTimeMillis(), "minEvictableIdleTimeMillis"); assertEquals(expected.getMinEvictableIdleTime(), actual.getMinEvictableIdleTime(), "minEvictableIdleTime"); assertEquals(expected.getNumTestsPerEvictionRun(), actual.getNumTestsPerEvictionRun(), "numTestsPerEvictionRun"); assertEquals(expected.getEvictorShutdownTimeoutMillis(), actual.getEvictorShutdownTimeoutMillis(), "evictorShutdownTimeoutMillis"); assertEquals(expected.getEvictorShutdownTimeout(), actual.getEvictorShutdownTimeout(), "evictorShutdownTimeout"); assertEquals(expected.getTimeBetweenEvictionRunsMillis(), actual.getTimeBetweenEvictionRunsMillis(), "timeBetweenEvictionRunsMillis"); assertEquals(expected.getTimeBetweenEvictionRuns(), actual.getTimeBetweenEvictionRuns(), "timeBetweenEvictionRuns"); } private void checkEvict(final boolean lifo) throws Exception { // yea this is hairy but it tests all the code paths in GOP.evict() genericObjectPool.setSoftMinEvictableIdleTime(Duration.ofMillis(10)); genericObjectPool.setMinIdle(2); genericObjectPool.setTestWhileIdle(true); genericObjectPool.setLifo(lifo); genericObjectPool.addObjects(5); genericObjectPool.evict(); simpleFactory.setEvenValid(false); simpleFactory.setOddValid(false); simpleFactory.setThrowExceptionOnActivate(true); genericObjectPool.evict(); genericObjectPool.addObjects(5); simpleFactory.setThrowExceptionOnActivate(false); simpleFactory.setThrowExceptionOnPassivate(true); genericObjectPool.evict(); simpleFactory.setThrowExceptionOnPassivate(false); simpleFactory.setEvenValid(true); simpleFactory.setOddValid(true); Thread.sleep(125); genericObjectPool.evict(); assertEquals(2, genericObjectPool.getNumIdle()); } private void checkEvictionOrder(final boolean lifo) throws Exception { checkEvictionOrderPart1(lifo); tearDown(); setUp(); checkEvictionOrderPart2(lifo); } private void checkEvictionOrderPart1(final boolean lifo) throws Exception { genericObjectPool.setNumTestsPerEvictionRun(2); genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100)); genericObjectPool.setLifo(lifo); for (int i = 0; i < 5; i++) { genericObjectPool.addObject(); Thread.sleep(100); } // Order, oldest to youngest, is "0", "1", ...,"4" genericObjectPool.evict(); // Should evict "0" and "1" final Object obj = genericObjectPool.borrowObject(); assertFalse(obj.equals("0"), "oldest not evicted"); assertFalse(obj.equals("1"), "second oldest not evicted"); // 2 should be next out for FIFO, 4 for LIFO assertEquals(lifo ? "4" : "2" , obj,"Wrong instance returned"); } private void checkEvictionOrderPart2(final boolean lifo) throws Exception { // Two eviction runs in sequence genericObjectPool.setNumTestsPerEvictionRun(2); genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100)); genericObjectPool.setLifo(lifo); for (int i = 0; i < 5; i++) { genericObjectPool.addObject(); Thread.sleep(100); } genericObjectPool.evict(); // Should evict "0" and "1" genericObjectPool.evict(); // Should evict "2" and "3" final Object obj = genericObjectPool.borrowObject(); assertEquals("4", obj,"Wrong instance remaining in pool"); } private void checkEvictorVisiting(final boolean lifo) throws Exception { VisitTracker obj; VisitTrackerFactory trackerFactory = new VisitTrackerFactory<>(); try (GenericObjectPool> trackerPool = new GenericObjectPool<>(trackerFactory)) { trackerPool.setNumTestsPerEvictionRun(2); trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1)); trackerPool.setTestWhileIdle(true); trackerPool.setLifo(lifo); trackerPool.setTestOnReturn(false); trackerPool.setTestOnBorrow(false); for (int i = 0; i < 8; i++) { trackerPool.addObject(); } trackerPool.evict(); // Visit oldest 2 - 0 and 1 obj = trackerPool.borrowObject(); trackerPool.returnObject(obj); obj = trackerPool.borrowObject(); trackerPool.returnObject(obj); // borrow, return, borrow, return // FIFO will move 0 and 1 to end // LIFO, 7 out, then in, then out, then in trackerPool.evict(); // Should visit 2 and 3 in either case for (int i = 0; i < 8; i++) { final VisitTracker tracker = trackerPool.borrowObject(); if (tracker.getId() >= 4) { assertEquals( 0, tracker.getValidateCount(),"Unexpected instance visited " + tracker.getId()); } else { assertEquals( 1, tracker.getValidateCount(), "Instance " + tracker.getId() + " visited wrong number of times."); } } } trackerFactory = new VisitTrackerFactory<>(); try (GenericObjectPool> trackerPool = new GenericObjectPool<>(trackerFactory)) { trackerPool.setNumTestsPerEvictionRun(3); trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1)); trackerPool.setTestWhileIdle(true); trackerPool.setLifo(lifo); trackerPool.setTestOnReturn(false); trackerPool.setTestOnBorrow(false); for (int i = 0; i < 8; i++) { trackerPool.addObject(); } trackerPool.evict(); // 0, 1, 2 trackerPool.evict(); // 3, 4, 5 obj = trackerPool.borrowObject(); trackerPool.returnObject(obj); obj = trackerPool.borrowObject(); trackerPool.returnObject(obj); obj = trackerPool.borrowObject(); trackerPool.returnObject(obj); // borrow, return, borrow, return // FIFO 3,4,5,6,7,0,1,2 // LIFO 7,6,5,4,3,2,1,0 // In either case, pointer should be at 6 trackerPool.evict(); // Should hit 6,7,0 - 0 for second time for (int i = 0; i < 8; i++) { final VisitTracker tracker = trackerPool.borrowObject(); if (tracker.getId() != 0) { assertEquals( 1, tracker.getValidateCount(), "Instance " + tracker.getId() + " visited wrong number of times."); } else { assertEquals( 2, tracker.getValidateCount(), "Instance " + tracker.getId() + " visited wrong number of times."); } } } // Randomly generate a pools with random numTests // and make sure evictor cycles through elements appropriately final int[] smallPrimes = { 2, 3, 5, 7 }; final Random random = new Random(); random.setSeed(System.currentTimeMillis()); for (int i = 0; i < 4; i++) { for (int j = 0; j < 5; j++) { try (GenericObjectPool> trackerPool = new GenericObjectPool<>(trackerFactory)) { trackerPool.setNumTestsPerEvictionRun(smallPrimes[i]); trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1)); trackerPool.setTestWhileIdle(true); trackerPool.setLifo(lifo); trackerPool.setTestOnReturn(false); trackerPool.setTestOnBorrow(false); trackerPool.setMaxIdle(-1); final int instanceCount = 10 + random.nextInt(20); trackerPool.setMaxTotal(instanceCount); for (int k = 0; k < instanceCount; k++) { trackerPool.addObject(); } // Execute a random number of evictor runs final int runs = 10 + random.nextInt(50); for (int k = 0; k < runs; k++) { trackerPool.evict(); } // Number of times evictor should have cycled through the pool final int cycleCount = (runs * trackerPool.getNumTestsPerEvictionRun()) / instanceCount; // Look at elements and make sure they are visited cycleCount // or cycleCount + 1 times VisitTracker tracker = null; int visitCount = 0; for (int k = 0; k < instanceCount; k++) { tracker = trackerPool.borrowObject(); assertTrue(trackerPool.getNumActive() <= trackerPool.getMaxTotal()); visitCount = tracker.getValidateCount(); assertTrue(visitCount >= cycleCount && visitCount <= cycleCount + 1); } } } } } private BasePooledObjectFactory createDefaultPooledObjectFactory() { return new BasePooledObjectFactory() { @Override public String create() { // fake return null; } @Override public PooledObject wrap(final String obj) { // fake return new DefaultPooledObject<>(obj); } }; } private BasePooledObjectFactory createNullPooledObjectFactory() { return new BasePooledObjectFactory() { @Override public String create() { // fake return null; } @Override public PooledObject wrap(final String obj) { // fake return null; } }; } private BasePooledObjectFactory createSlowObjectFactory(final long elapsedTimeMillis) { return new BasePooledObjectFactory() { @Override public String create() throws Exception { Thread.sleep(elapsedTimeMillis); return "created"; } @Override public PooledObject wrap(final String obj) { // fake return new DefaultPooledObject<>(obj); } }; } @Override protected Object getNthObject(final int n) { return String.valueOf(n); } @Override protected boolean isFifo() { return false; } @Override protected boolean isLifo() { return true; } @Override protected ObjectPool makeEmptyPool(final int minCap) { final GenericObjectPool mtPool = new GenericObjectPool<>(new SimpleFactory()); mtPool.setMaxTotal(minCap); mtPool.setMaxIdle(minCap); return mtPool; } @Override protected ObjectPool makeEmptyPool( final PooledObjectFactory fac) { return new GenericObjectPool<>(fac); } /** * Kicks off test threads, each of which will go through * borrow-return cycles with random delay times <= delay * in between. */ @SuppressWarnings({ "rawtypes", "unchecked" }) private void runTestThreads(final int numThreads, final int iterations, final int delay, final GenericObjectPool testPool) { final TestThread[] threads = new TestThread[numThreads]; for(int i=0;i(testPool,iterations,delay); final Thread t = new Thread(threads[i]); t.start(); } for(int i=0;i(simpleFactory); } @AfterEach public void tearDown() throws Exception { final ObjectName jmxName = genericObjectPool.getJmxName(); final String poolName = Objects.toString(jmxName, null); genericObjectPool.clear(); genericObjectPool.close(); genericObjectPool = null; simpleFactory = null; final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); final Set result = mbs.queryNames(new ObjectName( "org.apache.commoms.pool2:type=GenericObjectPool,*"), null); // There should be no registered pools at this point final int registeredPoolCount = result.size(); final StringBuilder msg = new StringBuilder("Current pool is: "); msg.append(poolName); msg.append(" Still open pools are: "); for (final ObjectName name : result) { // Clean these up ready for the next test msg.append(name.toString()); msg.append(" created via\n"); msg.append(mbs.getAttribute(name, "CreationStackTrace")); msg.append('\n'); mbs.unregisterMBean(name); } assertEquals( 0, registeredPoolCount,msg.toString()); // Make sure that EvictionTimer executor is shut down final Field evictorExecutorField = EvictionTimer.class.getDeclaredField("executor"); evictorExecutorField.setAccessible(true); assertNull(evictorExecutorField.get(null)); } /** * Check that a pool that starts an evictor, but is never closed does not * leave EvictionTimer executor running. Confirmation check is in teardown. */ @SuppressWarnings("deprecation") @Test public void testAbandonedPool() throws Exception { final GenericObjectPoolConfig config = new GenericObjectPoolConfig(); config.setJmxEnabled(false); GenericObjectPool abandoned = new GenericObjectPool<>(simpleFactory, config); abandoned.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); // Starts evictor assertEquals(abandoned.getRemoveAbandonedTimeout(), abandoned.getRemoveAbandonedTimeoutDuration().getSeconds()); // This is ugly, but forces gc to hit the pool final WeakReference ref = new WeakReference<>(abandoned); abandoned = null; while (ref.get() != null) { System.gc(); Thread.sleep(100); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testAddObject() throws Exception { assertEquals( 0, genericObjectPool.getNumIdle(),"should be zero idle"); genericObjectPool.addObject(); assertEquals( 1, genericObjectPool.getNumIdle(),"should be one idle"); assertEquals( 0, genericObjectPool.getNumActive(),"should be zero active"); final String obj = genericObjectPool.borrowObject(); assertEquals( 0, genericObjectPool.getNumIdle(),"should be zero idle"); assertEquals( 1, genericObjectPool.getNumActive(),"should be one active"); genericObjectPool.returnObject(obj); assertEquals( 1, genericObjectPool.getNumIdle(),"should be one idle"); assertEquals( 0, genericObjectPool.getNumActive(),"should be zero active"); } /* * Note: This test relies on timing for correct execution. There *should* be * enough margin for this to work correctly on most (all?) systems but be * aware of this if you see a failure of this test. */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testBorrowObjectFairness() throws Exception { final int numThreads = 40; final int maxTotal = 40; final GenericObjectPoolConfig config = new GenericObjectPoolConfig(); config.setMaxTotal(maxTotal); config.setMaxIdle(maxTotal); config.setFairness(true); config.setLifo(false); genericObjectPool = new GenericObjectPool(simpleFactory, config); // Exhaust the pool final String[] objects = new String[maxTotal]; for (int i = 0; i < maxTotal; i++) { objects[i] = genericObjectPool.borrowObject(); } // Start and park threads waiting to borrow objects final TestThread[] threads = new TestThread[numThreads]; for(int i=0;i pool = new GenericObjectPool<>(factory, new GenericObjectPoolConfig<>())) { final String s = pool.borrowObject(); // First borrow waits on create, so wait time should be at least 200 ms // Allow 100ms error in clock times assertTrue(pool.getMaxBorrowWaitTimeMillis() >= 100); assertTrue(pool.getMeanBorrowWaitTimeMillis() >= 100); pool.returnObject(s); pool.borrowObject(); // Second borrow does not have to wait on create, average should be about 100 assertTrue(pool.getMaxBorrowWaitTimeMillis() > 100); assertTrue(pool.getMeanBorrowWaitTimeMillis() < 200); assertTrue(pool.getMeanBorrowWaitTimeMillis() > 20); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testCloseMultiplePools1() throws Exception { try (final GenericObjectPool genericObjectPool2 = new GenericObjectPool<>(simpleFactory)) { genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND); genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND); } genericObjectPool.close(); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testCloseMultiplePools2() throws Exception { try (final GenericObjectPool genericObjectPool2 = new GenericObjectPool<>(simpleFactory)) { // Ensure eviction takes a long time, during which time EvictionTimer.executor's queue is empty simpleFactory.setDestroyLatency(1000L); // Ensure there is an object to evict, so that above latency takes effect genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND); genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND); genericObjectPool.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND); genericObjectPool2.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND); genericObjectPool.addObject(); genericObjectPool2.addObject(); // Close both pools } genericObjectPool.close(); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testConcurrentBorrowAndEvict() throws Exception { genericObjectPool.setMaxTotal(1); genericObjectPool.addObject(); for (int i = 0; i < 5000; i++) { final ConcurrentBorrowAndEvictThread one = new ConcurrentBorrowAndEvictThread(true); final ConcurrentBorrowAndEvictThread two = new ConcurrentBorrowAndEvictThread(false); one.start(); two.start(); one.join(); two.join(); genericObjectPool.returnObject(one.obj); /* Uncomment this for a progress indication if (i % 10 == 0) { System.out.println(i/10); } */ } } /** * POOL-231 - verify that concurrent invalidates of the same object do not * corrupt pool destroyCount. * * @throws Exception May occur in some failure modes */ @Test public void testConcurrentInvalidate() throws Exception { // Get allObjects and idleObjects loaded with some instances final int nObjects = 1000; genericObjectPool.setMaxTotal(nObjects); genericObjectPool.setMaxIdle(nObjects); final String[] obj = new String[nObjects]; for (int i = 0; i < nObjects; i++) { obj[i] = genericObjectPool.borrowObject(); } for (int i = 0; i < nObjects; i++) { if (i % 2 == 0) { genericObjectPool.returnObject(obj[i]); } } final int nThreads = 20; final int nIterations = 60; final InvalidateThread[] threads = new InvalidateThread[nThreads]; // Randomly generated list of distinct invalidation targets final ArrayList targets = new ArrayList<>(); final Random random = new Random(); for (int j = 0; j < nIterations; j++) { // Get a random invalidation target Integer targ = Integer.valueOf(random.nextInt(nObjects)); while (targets.contains(targ)) { targ = Integer.valueOf(random.nextInt(nObjects)); } targets.add(targ); // Launch nThreads threads all trying to invalidate the target for (int i = 0; i < nThreads; i++) { threads[i] = new InvalidateThread(genericObjectPool, obj[targ.intValue()]); } for (int i = 0; i < nThreads; i++) { new Thread(threads[i]).start(); } boolean done = false; while (!done) { done = true; for (int i = 0; i < nThreads; i++) { done = done && threads[i].complete(); } Thread.sleep(100); } } assertEquals(nIterations, genericObjectPool.getDestroyedCount()); } @Test public void testConstructorNullFactory() { // add dummy assert (won't be invoked because of IAE) to avoid "unused" warning assertThrows(IllegalArgumentException.class, () -> new GenericObjectPool<>(null)); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testConstructors() throws Exception { // Make constructor arguments all different from defaults final int minIdle = 2; final long maxWait = 3; final int maxIdle = 4; final int maxTotal = 5; final long minEvictableIdleTimeMillis = 6; final int numTestsPerEvictionRun = 7; final boolean testOnBorrow = true; final boolean testOnReturn = true; final boolean testWhileIdle = true; final long timeBetweenEvictionRunsMillis = 8; final boolean blockWhenExhausted = false; final boolean lifo = false; final PooledObjectFactory dummyFactory = new DummyFactory(); try (GenericObjectPool dummyPool = new GenericObjectPool<>(dummyFactory)) { assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_IDLE, dummyPool.getMaxIdle()); assertEquals(BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS, dummyPool.getMaxWaitMillis()); assertEquals(GenericObjectPoolConfig.DEFAULT_MIN_IDLE, dummyPool.getMinIdle()); assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL, dummyPool.getMaxTotal()); assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, dummyPool.getMinEvictableIdleTimeMillis()); assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME, dummyPool.getMinEvictableIdleTime()); assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN, dummyPool.getNumTestsPerEvictionRun()); assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW), Boolean.valueOf(dummyPool.getTestOnBorrow())); assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN), Boolean.valueOf(dummyPool.getTestOnReturn())); assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE), Boolean.valueOf(dummyPool.getTestWhileIdle())); assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, dummyPool.getTimeBetweenEvictionRunsMillis()); assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS, dummyPool.getTimeBetweenEvictionRuns()); assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED), Boolean.valueOf(dummyPool.getBlockWhenExhausted())); assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(dummyPool.getLifo())); } final GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setLifo(lifo); config.setMaxIdle(maxIdle); config.setMinIdle(minIdle); config.setMaxTotal(maxTotal); config.setMaxWaitMillis(maxWait); config.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); assertEquals(minEvictableIdleTimeMillis, config.getMinEvictableIdleTime().toMillis()); config.setNumTestsPerEvictionRun(numTestsPerEvictionRun); config.setTestOnBorrow(testOnBorrow); config.setTestOnReturn(testOnReturn); config.setTestWhileIdle(testWhileIdle); config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); assertEquals(timeBetweenEvictionRunsMillis, config.getTimeBetweenEvictionRuns().toMillis()); config.setBlockWhenExhausted(blockWhenExhausted); try (GenericObjectPool dummyPool = new GenericObjectPool<>(dummyFactory, config)) { assertEquals(maxIdle, dummyPool.getMaxIdle()); assertEquals(maxWait, dummyPool.getMaxWaitMillis()); assertEquals(minIdle, dummyPool.getMinIdle()); assertEquals(maxTotal, dummyPool.getMaxTotal()); assertEquals(minEvictableIdleTimeMillis, dummyPool.getMinEvictableIdleTimeMillis()); assertEquals(numTestsPerEvictionRun, dummyPool.getNumTestsPerEvictionRun()); assertEquals(Boolean.valueOf(testOnBorrow), Boolean.valueOf(dummyPool.getTestOnBorrow())); assertEquals(Boolean.valueOf(testOnReturn), Boolean.valueOf(dummyPool.getTestOnReturn())); assertEquals(Boolean.valueOf(testWhileIdle), Boolean.valueOf(dummyPool.getTestWhileIdle())); assertEquals(timeBetweenEvictionRunsMillis, dummyPool.getTimeBetweenEvictionRunsMillis()); assertEquals(Boolean.valueOf(blockWhenExhausted), Boolean.valueOf(dummyPool.getBlockWhenExhausted())); assertEquals(Boolean.valueOf(lifo), Boolean.valueOf(dummyPool.getLifo())); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testDefaultConfiguration() throws Exception { assertConfiguration(new GenericObjectPoolConfig<>(),genericObjectPool); } /** * Verifies that when a factory's makeObject produces instances that are not * discernible by equals, the pool can handle them. * * JIRA: POOL-283 */ @Test public void testEqualsIndiscernible() throws Exception { final HashSetFactory factory = new HashSetFactory(); try (final GenericObjectPool> pool = new GenericObjectPool<>(factory, new GenericObjectPoolConfig<>())) { final HashSet s1 = pool.borrowObject(); final HashSet s2 = pool.borrowObject(); pool.returnObject(s1); pool.returnObject(s2); } } @Test public void testErrorFactoryDoesNotBlockThreads() throws Exception { final CreateErrorFactory factory = new CreateErrorFactory(); try (final GenericObjectPool createFailFactoryPool = new GenericObjectPool<>(factory)) { createFailFactoryPool.setMaxTotal(1); // Try and borrow the first object from the pool final WaitingTestThread thread1 = new WaitingTestThread(createFailFactoryPool, 0); thread1.start(); // Wait for thread to reach semaphore while (!factory.hasQueuedThreads()) { Thread.sleep(200); } // Try and borrow the second object from the pool final WaitingTestThread thread2 = new WaitingTestThread(createFailFactoryPool, 0); thread2.start(); // Pool will not call factory since maximum number of object creations // are already queued. // Thread 2 will wait on an object being returned to the pool // Give thread 2 a chance to reach this state Thread.sleep(1000); // Release thread1 factory.release(); // Pre-release thread2 factory.release(); // Both threads should now complete. boolean threadRunning = true; int count = 0; while (threadRunning && count < 15) { threadRunning = thread1.isAlive(); threadRunning = thread2.isAlive(); Thread.sleep(200); count++; } assertFalse(thread1.isAlive()); assertFalse(thread2.isAlive()); assertTrue(thread1._thrown instanceof UnknownError); assertTrue(thread2._thrown instanceof UnknownError); } } /** * Tests addObject contention between ensureMinIdle triggered by * the Evictor with minIdle > 0 and borrowObject. * * @throws Exception May occur in some failure modes */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEvictAddObjects() throws Exception { simpleFactory.setMakeLatency(300); simpleFactory.setMaxTotal(2); genericObjectPool.setMaxTotal(2); genericObjectPool.setMinIdle(1); genericObjectPool.borrowObject(); // numActive = 1, numIdle = 0 // Create a test thread that will run once and try a borrow after // 150ms fixed delay final TestThread borrower = new TestThread<>(genericObjectPool, 1, 150, false); final Thread borrowerThread = new Thread(borrower); // Set evictor to run in 100 ms - will create idle instance genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); borrowerThread.start(); // Off to the races borrowerThread.join(); assertFalse(borrower.failed()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEvictFIFO() throws Exception { checkEvict(false); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEviction() throws Exception { genericObjectPool.setMaxIdle(500); genericObjectPool.setMaxTotal(500); genericObjectPool.setNumTestsPerEvictionRun(100); genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250)); genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); genericObjectPool.setTestWhileIdle(true); final String[] active = new String[500]; for (int i = 0; i < 500; i++) { active[i] = genericObjectPool.borrowObject(); } for (int i = 0; i < 500; i++) { genericObjectPool.returnObject(active[i]); } try { Thread.sleep(1000L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() < 500,"Should be less than 500 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() < 400,"Should be less than 400 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() < 300,"Should be less than 300 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() < 200,"Should be less than 200 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() < 100,"Should be less than 100 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertEquals(0,genericObjectPool.getNumIdle(),"Should be zero idle, found " + genericObjectPool.getNumIdle()); for (int i = 0; i < 500; i++) { active[i] = genericObjectPool.borrowObject(); } for (int i = 0; i < 500; i++) { genericObjectPool.returnObject(active[i]); } try { Thread.sleep(1000L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() < 500,"Should be less than 500 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() < 400,"Should be less than 400 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() < 300,"Should be less than 300 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() < 200,"Should be less than 200 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() < 100,"Should be less than 100 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(600L); } catch(final InterruptedException e) { } assertEquals(0,genericObjectPool.getNumIdle(),"Should be zero idle, found " + genericObjectPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEvictionInvalid() throws Exception { try (final GenericObjectPool invalidFactoryPool = new GenericObjectPool<>(new InvalidFactory())) { invalidFactoryPool.setMaxIdle(1); invalidFactoryPool.setMaxTotal(1); invalidFactoryPool.setTestOnBorrow(false); invalidFactoryPool.setTestOnReturn(false); invalidFactoryPool.setTestWhileIdle(true); invalidFactoryPool.setMinEvictableIdleTime(Duration.ofSeconds(100)); invalidFactoryPool.setNumTestsPerEvictionRun(1); final Object p = invalidFactoryPool.borrowObject(); invalidFactoryPool.returnObject(p); // Run eviction in a separate thread final Thread t = new EvictionThread<>(invalidFactoryPool); t.start(); // Sleep to make sure evictor has started Thread.sleep(300); try { invalidFactoryPool.borrowObject(1); } catch (final NoSuchElementException nsee) { // Ignore } // Make sure evictor has finished Thread.sleep(1000); // Should have an empty pool assertEquals( 0, invalidFactoryPool.getNumIdle(),"Idle count different than expected."); assertEquals( 0, invalidFactoryPool.getNumActive(),"Total count different than expected."); } } /** * Test to make sure evictor visits least recently used objects first, * regardless of FIFO/LIFO. * * JIRA: POOL-86 * * @throws Exception May occur in some failure modes */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEvictionOrder() throws Exception { checkEvictionOrder(false); tearDown(); setUp(); checkEvictionOrder(true); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEvictionPolicy() throws Exception { genericObjectPool.setMaxIdle(500); genericObjectPool.setMaxTotal(500); genericObjectPool.setNumTestsPerEvictionRun(500); genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250)); genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500)); genericObjectPool.setTestWhileIdle(true); // ClassNotFoundException try { genericObjectPool.setEvictionPolicyClassName(Long.toString(System.currentTimeMillis())); fail("setEvictionPolicyClassName must throw an error if the class name is invalid."); } catch (final IllegalArgumentException e) { // expected } // InstantiationException try { genericObjectPool.setEvictionPolicyClassName(java.io.Serializable.class.getName()); fail("setEvictionPolicyClassName must throw an error if the class name is invalid."); } catch (final IllegalArgumentException e) { // expected } // IllegalAccessException try { genericObjectPool.setEvictionPolicyClassName(java.util.Collections.class.getName()); fail("setEvictionPolicyClassName must throw an error if the class name is invalid."); } catch (final IllegalArgumentException e) { // expected } try { genericObjectPool.setEvictionPolicyClassName(java.lang.String.class.getName()); fail("setEvictionPolicyClassName must throw an error if a class that does not " + "implement EvictionPolicy is specified."); } catch (final IllegalArgumentException e) { // expected } genericObjectPool.setEvictionPolicy(new TestEvictionPolicy<>()); assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName()); genericObjectPool.setEvictionPolicyClassName(TestEvictionPolicy.class.getName()); assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName()); final String[] active = new String[500]; for (int i = 0; i < 500; i++) { active[i] = genericObjectPool.borrowObject(); } for (int i = 0; i < 500; i++) { genericObjectPool.returnObject(active[i]); } // Eviction policy ignores first 1500 attempts to evict and then always // evicts. After 1s, there should have been two runs of 500 tests so no // evictions try { Thread.sleep(1000L); } catch(final InterruptedException e) { } assertEquals( 500, genericObjectPool.getNumIdle(),"Should be 500 idle"); // A further 1s wasn't enough so allow 2s for the evictor to clear out // all of the idle objects. try { Thread.sleep(2000L); } catch(final InterruptedException e) { } assertEquals( 0, genericObjectPool.getNumIdle(),"Should be 0 idle"); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEvictionSoftMinIdle() throws Exception { class TimeTest extends BasePooledObjectFactory { private final long createTimeMillis; public TimeTest() { createTimeMillis = System.currentTimeMillis(); } @Override public TimeTest create() throws Exception { return new TimeTest(); } public long getCreateTimeMillis() { return createTimeMillis; } @Override public PooledObject wrap(final TimeTest value) { return new DefaultPooledObject<>(value); } } try (final GenericObjectPool timePool = new GenericObjectPool<>(new TimeTest())) { timePool.setMaxIdle(5); timePool.setMaxTotal(5); timePool.setNumTestsPerEvictionRun(5); timePool.setMinEvictableIdleTime(Duration.ofSeconds(3)); timePool.setSoftMinEvictableIdleTime(TestConstants.ONE_SECOND); timePool.setMinIdle(2); final TimeTest[] active = new TimeTest[5]; final Long[] creationTime = new Long[5]; for (int i = 0; i < 5; i++) { active[i] = timePool.borrowObject(); creationTime[i] = Long.valueOf((active[i]).getCreateTimeMillis()); } for (int i = 0; i < 5; i++) { timePool.returnObject(active[i]); } // Soft evict all but minIdle(2) Thread.sleep(1500L); timePool.evict(); assertEquals( 2, timePool.getNumIdle(),"Idle count different than expected."); // Hard evict the rest. Thread.sleep(2000L); timePool.evict(); assertEquals( 0, timePool.getNumIdle(),"Idle count different than expected."); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEvictionWithNegativeNumTests() throws Exception { // when numTestsPerEvictionRun is negative, it represents a fraction of the idle objects to test genericObjectPool.setMaxIdle(6); genericObjectPool.setMaxTotal(6); genericObjectPool.setNumTestsPerEvictionRun(-2); genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50)); genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); final String[] active = new String[6]; for (int i = 0; i < 6; i++) { active[i] = genericObjectPool.borrowObject(); } for (int i = 0; i < 6; i++) { genericObjectPool.returnObject(active[i]); } try { Thread.sleep(100L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() <= 6,"Should at most 6 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(100L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() <= 3,"Should at most 3 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(100L); } catch(final InterruptedException e) { } assertTrue(genericObjectPool.getNumIdle() <= 2,"Should be at most 2 idle, found " + genericObjectPool.getNumIdle()); try { Thread.sleep(100L); } catch(final InterruptedException e) { } assertEquals(0,genericObjectPool.getNumIdle(),"Should be zero idle, found " + genericObjectPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEvictLIFO() throws Exception { checkEvict(true); } /** * Verifies that the evictor visits objects in expected order * and frequency. * * @throws Exception May occur in some failure modes */ @Test public void testEvictorVisiting() throws Exception { checkEvictorVisiting(true); checkEvictorVisiting(false); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testEvictWhileEmpty() throws Exception { genericObjectPool.evict(); genericObjectPool.evict(); genericObjectPool.close(); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testExceptionOnActivateDuringBorrow() throws Exception { final String obj1 = genericObjectPool.borrowObject(); final String obj2 = genericObjectPool.borrowObject(); genericObjectPool.returnObject(obj1); genericObjectPool.returnObject(obj2); simpleFactory.setThrowExceptionOnActivate(true); simpleFactory.setEvenValid(false); // Activation will now throw every other time // First attempt throws, but loop continues and second succeeds final String obj = genericObjectPool.borrowObject(); assertEquals(1, genericObjectPool.getNumActive()); assertEquals(0, genericObjectPool.getNumIdle()); genericObjectPool.returnObject(obj); simpleFactory.setValid(false); // Validation will now fail on activation when borrowObject returns // an idle instance, and then when attempting to create a new instance try { genericObjectPool.borrowObject(); fail("Expecting NoSuchElementException"); } catch (final NoSuchElementException ex) { // expected } assertEquals(0, genericObjectPool.getNumActive()); assertEquals(0, genericObjectPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testExceptionOnDestroyDuringBorrow() throws Exception { simpleFactory.setThrowExceptionOnDestroy(true); genericObjectPool.setTestOnBorrow(true); genericObjectPool.borrowObject(); simpleFactory.setValid(false); // Make validation fail on next borrow attempt try { genericObjectPool.borrowObject(); fail("Expecting NoSuchElementException"); } catch (final NoSuchElementException ex) { // expected } assertEquals(1, genericObjectPool.getNumActive()); assertEquals(0, genericObjectPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testExceptionOnDestroyDuringReturn() throws Exception { simpleFactory.setThrowExceptionOnDestroy(true); genericObjectPool.setTestOnReturn(true); final String obj1 = genericObjectPool.borrowObject(); genericObjectPool.borrowObject(); simpleFactory.setValid(false); // Make validation fail genericObjectPool.returnObject(obj1); assertEquals(1, genericObjectPool.getNumActive()); assertEquals(0, genericObjectPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testExceptionOnPassivateDuringReturn() throws Exception { final String obj = genericObjectPool.borrowObject(); simpleFactory.setThrowExceptionOnPassivate(true); genericObjectPool.returnObject(obj); assertEquals(0,genericObjectPool.getNumIdle()); } @Test public void testFailingFactoryDoesNotBlockThreads() throws Exception { final CreateFailFactory factory = new CreateFailFactory(); try (final GenericObjectPool createFailFactoryPool = new GenericObjectPool<>(factory)) { createFailFactoryPool.setMaxTotal(1); // Try and borrow the first object from the pool final WaitingTestThread thread1 = new WaitingTestThread(createFailFactoryPool, 0); thread1.start(); // Wait for thread to reach semaphore while (!factory.hasQueuedThreads()) { Thread.sleep(200); } // Try and borrow the second object from the pool final WaitingTestThread thread2 = new WaitingTestThread(createFailFactoryPool, 0); thread2.start(); // Pool will not call factory since maximum number of object creations // are already queued. // Thread 2 will wait on an object being returned to the pool // Give thread 2 a chance to reach this state Thread.sleep(1000); // Release thread1 factory.release(); // Pre-release thread2 factory.release(); // Both threads should now complete. boolean threadRunning = true; int count = 0; while (threadRunning && count < 15) { threadRunning = thread1.isAlive(); threadRunning = thread2.isAlive(); Thread.sleep(200); count++; } assertFalse(thread1.isAlive()); assertFalse(thread2.isAlive()); assertTrue(thread1._thrown instanceof UnsupportedCharsetException); assertTrue(thread2._thrown instanceof UnsupportedCharsetException); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testFIFO() throws Exception { genericObjectPool.setLifo(false); genericObjectPool.addObject(); // "0" genericObjectPool.addObject(); // "1" genericObjectPool.addObject(); // "2" assertEquals( "0", genericObjectPool.borrowObject(),"Oldest"); assertEquals( "1", genericObjectPool.borrowObject(),"Middle"); assertEquals( "2", genericObjectPool.borrowObject(),"Youngest"); final String o = genericObjectPool.borrowObject(); assertEquals( "3", o,"new-3"); genericObjectPool.returnObject(o); assertEquals( o, genericObjectPool.borrowObject(),"returned-3"); assertEquals( "4", genericObjectPool.borrowObject(),"new-4"); } @Test public void testGetFactoryType_DefaultPooledObjectFactory() { try (final GenericObjectPool pool = new GenericObjectPool<>(createDefaultPooledObjectFactory())) { assertNotNull((pool.getFactoryType())); } } @Test public void testGetFactoryType_NullPooledObjectFactory() { try (final GenericObjectPool pool = new GenericObjectPool<>(createNullPooledObjectFactory())) { assertNotNull((pool.getFactoryType())); } } @Test public void testGetFactoryType_PoolUtilsSynchronizedDefaultPooledFactory() { try (final GenericObjectPool pool = new GenericObjectPool<>( PoolUtils.synchronizedPooledFactory(createDefaultPooledObjectFactory()))) { assertNotNull((pool.getFactoryType())); } } @Test public void testGetFactoryType_PoolUtilsSynchronizedNullPooledFactory() { try (final GenericObjectPool pool = new GenericObjectPool<>( PoolUtils.synchronizedPooledFactory(createNullPooledObjectFactory()))) { assertNotNull((pool.getFactoryType())); } } @Test public void testGetFactoryType_SynchronizedDefaultPooledObjectFactory() { try (final GenericObjectPool pool = new GenericObjectPool<>( new TestSynchronizedPooledObjectFactory<>(createDefaultPooledObjectFactory()))) { assertNotNull((pool.getFactoryType())); } } @Test public void testGetFactoryType_SynchronizedNullPooledObjectFactory() { try (final GenericObjectPool pool = new GenericObjectPool<>( new TestSynchronizedPooledObjectFactory<>(createNullPooledObjectFactory()))) { assertNotNull((pool.getFactoryType())); } } /** * Verify that threads waiting on a depleted pool get served when a checked out object is * invalidated. * * JIRA: POOL-240 * * @throws Exception May occur in some failure modes */ @Test public void testInvalidateFreesCapacity() throws Exception { final SimpleFactory factory = new SimpleFactory(); try (final GenericObjectPool pool = new GenericObjectPool<>(factory)) { pool.setMaxTotal(2); pool.setMaxWaitMillis(500); // Borrow an instance and hold if for 5 seconds final WaitingTestThread thread1 = new WaitingTestThread(pool, 5000); thread1.start(); // Borrow another instance final String obj = pool.borrowObject(); // Launch another thread - will block, but fail in 500 ms final WaitingTestThread thread2 = new WaitingTestThread(pool, 100); thread2.start(); // Invalidate the object borrowed by this thread - should allow thread2 to create Thread.sleep(20); pool.invalidateObject(obj); Thread.sleep(600); // Wait for thread2 to timeout if (thread2._thrown != null) { fail(thread2._thrown.toString()); } } } /** * Ensure the pool is registered. */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testJmxRegistration() { final ObjectName oname = genericObjectPool.getJmxName(); final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); final Set result = mbs.queryNames(oname, null); assertEquals(1, result.size()); genericObjectPool.jmxUnregister(); final GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setJmxEnabled(false); try (final GenericObjectPool poolWithoutJmx = new GenericObjectPool<>(simpleFactory, config)) { assertNull(poolWithoutJmx.getJmxName()); config.setJmxEnabled(true); poolWithoutJmx.jmxUnregister(); } config.setJmxNameBase(null); try (final GenericObjectPool poolWithDefaultJmxNameBase = new GenericObjectPool<>(simpleFactory, config)) { assertNotNull(poolWithDefaultJmxNameBase.getJmxName()); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testLIFO() throws Exception { final String o; genericObjectPool.setLifo(true); genericObjectPool.addObject(); // "0" genericObjectPool.addObject(); // "1" genericObjectPool.addObject(); // "2" assertEquals( "2", genericObjectPool.borrowObject(),"Youngest"); assertEquals( "1", genericObjectPool.borrowObject(),"Middle"); assertEquals( "0", genericObjectPool.borrowObject(),"Oldest"); o = genericObjectPool.borrowObject(); assertEquals( "3", o,"new-3"); genericObjectPool.returnObject(o); assertEquals( o, genericObjectPool.borrowObject(),"returned-3"); assertEquals( "4", genericObjectPool.borrowObject(),"new-4"); } /** * Test the following scenario: * Thread 1 borrows an instance * Thread 2 starts to borrow another instance before thread 1 returns its instance * Thread 1 returns its instance while thread 2 is validating its newly created instance * The test verifies that the instance created by Thread 2 is not leaked. * * @throws Exception May occur in some failure modes */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMakeConcurrentWithReturn() throws Exception { genericObjectPool.setTestOnBorrow(true); simpleFactory.setValid(true); // Borrow and return an instance, with a short wait final WaitingTestThread thread1 = new WaitingTestThread(genericObjectPool, 200); thread1.start(); Thread.sleep(50); // wait for validation to succeed // Slow down validation and borrow an instance simpleFactory.setValidateLatency(400); final String instance = genericObjectPool.borrowObject(); // Now make sure that we have not leaked an instance assertEquals(simpleFactory.getMakeCounter(), genericObjectPool.getNumIdle() + 1); genericObjectPool.returnObject(instance); assertEquals(simpleFactory.getMakeCounter(), genericObjectPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxIdle() throws Exception { genericObjectPool.setMaxTotal(100); genericObjectPool.setMaxIdle(8); final String[] active = new String[100]; for(int i=0;i<100;i++) { active[i] = genericObjectPool.borrowObject(); } assertEquals(100,genericObjectPool.getNumActive()); assertEquals(0,genericObjectPool.getNumIdle()); for(int i=0;i<100;i++) { genericObjectPool.returnObject(active[i]); assertEquals(99 - i,genericObjectPool.getNumActive()); assertEquals((i < 8 ? i+1 : 8),genericObjectPool.getNumIdle()); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxIdleZero() throws Exception { genericObjectPool.setMaxTotal(100); genericObjectPool.setMaxIdle(0); final String[] active = new String[100]; for(int i=0;i<100;i++) { active[i] = genericObjectPool.borrowObject(); } assertEquals(100,genericObjectPool.getNumActive()); assertEquals(0,genericObjectPool.getNumIdle()); for(int i=0;i<100;i++) { genericObjectPool.returnObject(active[i]); assertEquals(99 - i,genericObjectPool.getNumActive()); assertEquals(0, genericObjectPool.getNumIdle()); } } /** * Showcasing a possible deadlock situation as reported in POOL-356 */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) @SuppressWarnings("rawtypes") public void testMaxIdleZeroUnderLoad() { // Config final int numThreads = 199; // And main thread makes a round 200. final int numIter = 20; final int delay = 25; final int maxTotal = 10; simpleFactory.setMaxTotal(maxTotal); genericObjectPool.setMaxTotal(maxTotal); genericObjectPool.setBlockWhenExhausted(true); genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(-1)); // this is important to trigger POOL-356 genericObjectPool.setMaxIdle(0); // Start threads to borrow objects final TestThread[] threads = new TestThread[numThreads]; for(int i=0;i(genericObjectPool, numIter * 2, delay * 2); final Thread t = new Thread(threads[i]); t.start(); } // Give the threads a chance to start doing some work try { Thread.sleep(100); } catch(final InterruptedException e) { // ignored } for (int i = 0; i < numIter; i++) { String obj = null; try { try { Thread.sleep(delay); } catch(final InterruptedException e) { // ignored } obj = genericObjectPool.borrowObject(); // Under load, observed _numActive > _maxTotal if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) { throw new IllegalStateException("Too many active objects"); } try { Thread.sleep(delay); } catch(final InterruptedException e) { // ignored } } catch (final Exception e) { // Shouldn't happen e.printStackTrace(); fail("Exception on borrow"); } finally { if (obj != null) { try { genericObjectPool.returnObject(obj); } catch (final Exception e) { // Ignore } } } } for(int i=0;i(genericObjectPool, numIter * 2, delay * 2); final Thread t = new Thread(threads[i]); t.start(); } // Give the threads a chance to start doing some work try { Thread.sleep(5000); } catch(final InterruptedException e) { // ignored } for (int i = 0; i < numIter; i++) { String obj = null; try { try { Thread.sleep(delay); } catch(final InterruptedException e) { // ignored } obj = genericObjectPool.borrowObject(); // Under load, observed _numActive > _maxTotal if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) { throw new IllegalStateException("Too many active objects"); } try { Thread.sleep(delay); } catch(final InterruptedException e) { // ignored } } catch (final Exception e) { // Shouldn't happen e.printStackTrace(); fail("Exception on borrow"); } finally { if (obj != null) { try { genericObjectPool.returnObject(obj); } catch (final Exception e) { // Ignore } } } } for (int i = 0; i < numThreads; i++) { while(!(threads[i]).complete()) { try { Thread.sleep(500L); } catch(final InterruptedException e) { // ignored } } if(threads[i].failed()) { fail("Thread " + i + " failed: " + threads[i]._error.toString()); } } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxTotalZero() throws Exception { genericObjectPool.setMaxTotal(0); genericObjectPool.setBlockWhenExhausted(false); try { genericObjectPool.borrowObject(); fail("Expected NoSuchElementException"); } catch(final NoSuchElementException e) { // expected } } /* * Test multi-threaded pool access. * Multiple threads, but maxTotal only allows half the threads to succeed. * * This test was prompted by Continuum build failures in the Commons DBCP test case: * TestPerUserPoolDataSource.testMultipleThreads2() * Let's see if the this fails on Continuum too! */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMaxWaitMultiThreaded() throws Exception { final long maxWait = 500; // wait for connection final long holdTime = 2 * maxWait; // how long to hold connection final int threads = 10; // number of threads to grab the object initially genericObjectPool.setBlockWhenExhausted(true); genericObjectPool.setMaxWaitMillis(maxWait); genericObjectPool.setMaxTotal(threads); // Create enough threads so half the threads will have to wait final WaitingTestThread[] wtt = new WaitingTestThread[threads * 2]; for (int i = 0; i < wtt.length; i++) { wtt[i] = new WaitingTestThread(genericObjectPool,holdTime); } final long originMillis = System.currentTimeMillis() - 1000; for (final WaitingTestThread element : wtt) { element.start(); } int failed = 0; for (final WaitingTestThread element : wtt) { element.join(); if (element._thrown != null){ failed++; } } if (DISPLAY_THREAD_DETAILS || wtt.length/2 != failed){ System.out.println( "MaxWait: " + maxWait + " HoldTime: " + holdTime + " MaxTotal: " + threads + " Threads: " + wtt.length + " Failed: " + failed ); for (final WaitingTestThread wt : wtt) { System.out.println( "PreBorrow: " + (wt.preBorrowMillis - originMillis) + " PostBorrow: " + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - originMillis : -1) + " BorrowTime: " + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - wt.preBorrowMillis : -1) + " PostReturn: " + (wt.postReturnMillis != 0 ? wt.postReturnMillis - originMillis : -1) + " Ended: " + (wt.endedMillis - originMillis) + " ObjId: " + wt.objectId ); } } assertEquals(wtt.length / 2, failed,"Expected half the threads to fail"); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMinIdle() throws Exception { genericObjectPool.setMaxIdle(500); genericObjectPool.setMinIdle(5); genericObjectPool.setMaxTotal(10); genericObjectPool.setNumTestsPerEvictionRun(0); genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50)); genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); genericObjectPool.setTestWhileIdle(true); try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle()); final String[] active = new String[5]; active[0] = genericObjectPool.borrowObject(); try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle()); for(int i=1 ; i<5 ; i++) { active[i] = genericObjectPool.borrowObject(); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle()); for(int i=0 ; i<5 ; i++) { genericObjectPool.returnObject(active[i]); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testMinIdleMaxTotal() throws Exception { genericObjectPool.setMaxIdle(500); genericObjectPool.setMinIdle(5); genericObjectPool.setMaxTotal(10); genericObjectPool.setNumTestsPerEvictionRun(0); genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50)); genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); genericObjectPool.setTestWhileIdle(true); try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle()); final String[] active = new String[10]; try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle()); for (int i = 0; i < 5; i++) { active[i] = genericObjectPool.borrowObject(); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle()); for(int i = 0 ; i < 5 ; i++) { genericObjectPool.returnObject(active[i]); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle()); for (int i = 0; i < 10; i++) { active[i] = genericObjectPool.borrowObject(); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(0, genericObjectPool.getNumIdle(), "Should be 0 idle, found " + genericObjectPool.getNumIdle()); for (int i = 0; i < 10; i++) { genericObjectPool.returnObject(active[i]); } try { Thread.sleep(150L); } catch(final InterruptedException e) { } assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle()); } /** * Verifies that returning an object twice (without borrow in between) causes ISE * but does not re-validate or re-passivate the instance. * * JIRA: POOL-285 */ @Test public void testMultipleReturn() throws Exception { final WaiterFactory factory = new WaiterFactory<>(0, 0, 0, 0, 0, 0); try (final GenericObjectPool pool = new GenericObjectPool<>(factory)) { pool.setTestOnReturn(true); final Waiter waiter = pool.borrowObject(); pool.returnObject(waiter); assertEquals(1, waiter.getValidationCount()); assertEquals(1, waiter.getPassivationCount()); try { pool.returnObject(waiter); fail("Expecting IllegalStateException from multiple return"); } catch (final IllegalStateException ex) { // Exception is expected, now check no repeat validation/passivation assertEquals(1, waiter.getValidationCount()); assertEquals(1, waiter.getPassivationCount()); } } } // POOL-248 @Test public void testMultipleReturnOfSameObject() throws Exception { try (final GenericObjectPool pool = new GenericObjectPool<>(simpleFactory, new GenericObjectPoolConfig<>())) { assertEquals(0, pool.getNumActive()); assertEquals(0, pool.getNumIdle()); final String obj = pool.borrowObject(); assertEquals(1, pool.getNumActive()); assertEquals(0, pool.getNumIdle()); pool.returnObject(obj); assertEquals(0, pool.getNumActive()); assertEquals(1, pool.getNumIdle()); assertThrows(IllegalStateException.class, () -> pool.returnObject(obj)); assertEquals(0, pool.getNumActive()); assertEquals(1, pool.getNumIdle()); } } /** * Verifies that when a borrowed object is mutated in a way that does not * preserve equality and hashcode, the pool can recognized it on return. * * JIRA: POOL-284 */ @Test public void testMutable() throws Exception { final HashSetFactory factory = new HashSetFactory(); try (final GenericObjectPool> pool = new GenericObjectPool<>(factory, new GenericObjectPoolConfig<>())) { final HashSet s1 = pool.borrowObject(); final HashSet s2 = pool.borrowObject(); s1.add("One"); s2.add("One"); pool.returnObject(s1); pool.returnObject(s2); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testNegativeMaxTotal() throws Exception { genericObjectPool.setMaxTotal(-1); genericObjectPool.setBlockWhenExhausted(false); final String obj = genericObjectPool.borrowObject(); assertEquals(getNthObject(0),obj); genericObjectPool.returnObject(obj); } /** * Verifies that concurrent threads never "share" instances */ @Test public void testNoInstanceOverlap() { final int maxTotal = 5; final int numThreads = 100; final int delay = 1; final int iterations = 1000; final AtomicIntegerFactory factory = new AtomicIntegerFactory(); try (final GenericObjectPool pool = new GenericObjectPool<>(factory)) { pool.setMaxTotal(maxTotal); pool.setMaxIdle(maxTotal); pool.setTestOnBorrow(true); pool.setBlockWhenExhausted(true); pool.setMaxWaitMillis(-1); runTestThreads(numThreads, iterations, delay, pool); assertEquals(0, pool.getDestroyedByBorrowValidationCount()); } } /** * POOL-376 */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testNoInvalidateNPE() throws Exception { genericObjectPool.setMaxTotal(1); genericObjectPool.setTestOnCreate(true); genericObjectPool.setMaxWaitMillis(-1); final String obj = genericObjectPool.borrowObject(); // Make validation fail - this will cause create() to return null simpleFactory.setValid(false); // Create a take waiter final WaitingTestThread wtt = new WaitingTestThread(genericObjectPool, 200); wtt.start(); // Give wtt time to start Thread.sleep(200); genericObjectPool.invalidateObject(obj); // Now allow create to succeed so waiter can be served simpleFactory.setValid(true); } public void testPreparePool() throws Exception { genericObjectPool.setMinIdle(1); genericObjectPool.setMaxTotal(1); genericObjectPool.preparePool(); assertEquals(1, genericObjectPool.getNumIdle()); final String obj = genericObjectPool.borrowObject(); genericObjectPool.preparePool(); assertEquals(0, genericObjectPool.getNumIdle()); genericObjectPool.setMinIdle(0); genericObjectPool.returnObject(obj); genericObjectPool.preparePool(); assertEquals(0, genericObjectPool.getNumIdle()); } @Test/* maxWaitMillis x2 + padding */ @Timeout(value = 1200, unit = TimeUnit.MILLISECONDS) public void testReturnBorrowObjectWithingMaxWaitMillis() throws Exception { final long maxWaitMillis = 500; try (final GenericObjectPool createSlowObjectFactoryPool = new GenericObjectPool<>( createSlowObjectFactory(60000))) { createSlowObjectFactoryPool.setMaxTotal(1); createSlowObjectFactoryPool.setMaxWaitMillis(maxWaitMillis); // thread1 tries creating a slow object to make pool full. final WaitingTestThread thread1 = new WaitingTestThread(createSlowObjectFactoryPool, 0); thread1.start(); // Wait for thread1's reaching to create(). Thread.sleep(100); // another one tries borrowObject. It should return within maxWaitMillis. try { createSlowObjectFactoryPool.borrowObject(maxWaitMillis); fail("borrowObject must fail due to timeout by maxWaitMillis"); } catch (final NoSuchElementException e) { // ignore } assertTrue(thread1.isAlive()); } } /** * This is the test case for POOL-263. It is disabled since it will always * pass without artificial delay being injected into GOP.returnObject() and * a way to this hasn't currently been found that doesn't involve * polluting the GOP implementation. The artificial delay needs to be * inserted just before the final call to isLifo() in the returnObject() * method. */ //@Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testReturnObject() throws Exception { genericObjectPool.setMaxTotal(1); genericObjectPool.setMaxIdle(-1); final String active = genericObjectPool.borrowObject(); assertEquals(1, genericObjectPool.getNumActive()); assertEquals(0, genericObjectPool.getNumIdle()); final Thread t = new Thread(() -> genericObjectPool.close()); t.start(); genericObjectPool.returnObject(active); // Wait for the close() thread to complete while (t.isAlive()) { Thread.sleep(50); } assertEquals(0, genericObjectPool.getNumIdle()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testSetConfig() throws Exception { final GenericObjectPoolConfig expected = new GenericObjectPoolConfig<>(); assertConfiguration(expected,genericObjectPool); expected.setMaxTotal(2); expected.setMaxIdle(3); expected.setMaxWaitMillis(5L); expected.setMinEvictableIdleTime(Duration.ofMillis(7L)); expected.setNumTestsPerEvictionRun(9); expected.setTestOnCreate(true); expected.setTestOnBorrow(true); expected.setTestOnReturn(true); expected.setTestWhileIdle(true); expected.setTimeBetweenEvictionRuns(Duration.ofMillis(11L)); expected.setBlockWhenExhausted(false); genericObjectPool.setConfig(expected); assertConfiguration(expected,genericObjectPool); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testSettersAndGetters() throws Exception { { // The object receives an Exception during its creation to prevent // memory leaks. See BaseGenericObjectPool constructor for more details. assertEquals(false, "".equals(genericObjectPool.getCreationStackTrace())); } { assertEquals(0, genericObjectPool.getBorrowedCount()); } { assertEquals(0, genericObjectPool.getReturnedCount()); } { assertEquals(0, genericObjectPool.getCreatedCount()); } { assertEquals(0, genericObjectPool.getDestroyedCount()); } { assertEquals(0, genericObjectPool.getDestroyedByEvictorCount()); } { assertEquals(0, genericObjectPool.getDestroyedByBorrowValidationCount()); } { assertEquals(0, genericObjectPool.getMeanActiveTimeMillis()); } { assertEquals(0, genericObjectPool.getMeanIdleTimeMillis()); } { assertEquals(0, genericObjectPool.getMeanBorrowWaitTimeMillis()); } { assertEquals(0, genericObjectPool.getMaxBorrowWaitTimeMillis()); } { assertEquals(0, genericObjectPool.getNumIdle()); } { genericObjectPool.setMaxTotal(123); assertEquals(123,genericObjectPool.getMaxTotal()); } { genericObjectPool.setMaxIdle(12); assertEquals(12,genericObjectPool.getMaxIdle()); } { genericObjectPool.setMaxWaitMillis(1234L); assertEquals(1234L,genericObjectPool.getMaxWaitMillis()); } { genericObjectPool.setMinEvictableIdleTimeMillis(12345L); assertEquals(12345L,genericObjectPool.getMinEvictableIdleTimeMillis()); assertEquals(12345L,genericObjectPool.getMinEvictableIdleTime().toMillis()); } { genericObjectPool.setNumTestsPerEvictionRun(11); assertEquals(11,genericObjectPool.getNumTestsPerEvictionRun()); } { genericObjectPool.setTestOnBorrow(true); assertTrue(genericObjectPool.getTestOnBorrow()); genericObjectPool.setTestOnBorrow(false); assertFalse(genericObjectPool.getTestOnBorrow()); } { genericObjectPool.setTestOnReturn(true); assertTrue(genericObjectPool.getTestOnReturn()); genericObjectPool.setTestOnReturn(false); assertFalse(genericObjectPool.getTestOnReturn()); } { genericObjectPool.setTestWhileIdle(true); assertTrue(genericObjectPool.getTestWhileIdle()); genericObjectPool.setTestWhileIdle(false); assertFalse(genericObjectPool.getTestWhileIdle()); } { genericObjectPool.setTimeBetweenEvictionRunsMillis(11235L); assertEquals(11235L,genericObjectPool.getTimeBetweenEvictionRunsMillis()); assertEquals(11235L,genericObjectPool.getTimeBetweenEvictionRuns().toMillis()); } { genericObjectPool.setSoftMinEvictableIdleTimeMillis(12135L); assertEquals(12135L,genericObjectPool.getSoftMinEvictableIdleTimeMillis()); assertEquals(12135L,genericObjectPool.getSoftMinEvictableIdleTime().toMillis()); } { genericObjectPool.setBlockWhenExhausted(true); assertTrue(genericObjectPool.getBlockWhenExhausted()); genericObjectPool.setBlockWhenExhausted(false); assertFalse(genericObjectPool.getBlockWhenExhausted()); } } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testStartAndStopEvictor() throws Exception { // set up pool without evictor genericObjectPool.setMaxIdle(6); genericObjectPool.setMaxTotal(6); genericObjectPool.setNumTestsPerEvictionRun(6); genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100)); for (int j = 0; j < 2; j++) { // populate the pool { final String[] active = new String[6]; for (int i = 0; i < 6; i++) { active[i] = genericObjectPool.borrowObject(); } for (int i = 0; i < 6; i++) { genericObjectPool.returnObject(active[i]); } } // note that it stays populated assertEquals(6,genericObjectPool.getNumIdle(),"Should have 6 idle"); // start the evictor genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(50)); // wait a second (well, .2 seconds) try { Thread.sleep(200L); } catch(final InterruptedException e) { } // assert that the evictor has cleared out the pool assertEquals(0,genericObjectPool.getNumIdle(),"Should have 0 idle"); // stop the evictor genericObjectPool.startEvictor(Duration.ZERO); } } @Test public void testSwallowedExceptionListener() { genericObjectPool.setSwallowedExceptionListener(null); // must simply return final List swallowedExceptions = new ArrayList<>(); /* * A simple listener, that will throw a OOM on 3rd exception. */ final SwallowedExceptionListener listener = e -> { if (swallowedExceptions.size() == 2) { throw new OutOfMemoryError(); } swallowedExceptions.add(e); }; genericObjectPool.setSwallowedExceptionListener(listener); final Exception e1 = new Exception(); final Exception e2 = new ArrayIndexOutOfBoundsException(); genericObjectPool.swallowException(e1); genericObjectPool.swallowException(e2); try { genericObjectPool.swallowException(e1); fail("Not supposed to get here"); } catch (final OutOfMemoryError oom) { // expected } assertEquals(2, swallowedExceptions.size()); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testThreaded1() throws Exception { genericObjectPool.setMaxTotal(15); genericObjectPool.setMaxIdle(15); genericObjectPool.setMaxWaitMillis(1000L); runTestThreads(20, 100, 50, genericObjectPool); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testTimeoutNoLeak() throws Exception { genericObjectPool.setMaxTotal(2); genericObjectPool.setMaxWaitMillis(10); genericObjectPool.setBlockWhenExhausted(true); final String obj = genericObjectPool.borrowObject(); final String obj2 = genericObjectPool.borrowObject(); try { genericObjectPool.borrowObject(); fail("Expecting NoSuchElementException"); } catch (final NoSuchElementException ex) { // expected } genericObjectPool.returnObject(obj2); genericObjectPool.returnObject(obj); genericObjectPool.borrowObject(); genericObjectPool.borrowObject(); } /** * Tests POOL-361 */ @Test public void testValidateOnCreate() throws Exception { genericObjectPool.setTestOnCreate(true); genericObjectPool.addObject(); assertEquals(1, simpleFactory.validateCounter); } /** * Tests POOL-361 */ @Test public void testValidateOnCreateFailure() throws Exception { genericObjectPool.setTestOnCreate(true); genericObjectPool.setTestOnBorrow(false); genericObjectPool.setMaxTotal(2); simpleFactory.setValid(false); // Make sure failed validations do not leak capacity genericObjectPool.addObject(); genericObjectPool.addObject(); assertEquals(0, genericObjectPool.getNumIdle()); assertEquals(0, genericObjectPool.getNumActive()); simpleFactory.setValid(true); final String obj = genericObjectPool.borrowObject(); assertNotNull(obj); genericObjectPool.addObject(); // Should have one idle, one out now assertEquals(1, genericObjectPool.getNumIdle()); assertEquals(1, genericObjectPool.getNumActive()); } /** * Verify that threads waiting on a depleted pool get served when a returning object fails * validation. * * JIRA: POOL-240 * * @throws Exception May occur in some failure modes */ @Test public void testValidationFailureOnReturnFreesCapacity() throws Exception { final SimpleFactory factory = new SimpleFactory(); factory.setValid(false); // Validate will always fail factory.setValidationEnabled(true); try (final GenericObjectPool pool = new GenericObjectPool<>(factory)) { pool.setMaxTotal(2); pool.setMaxWaitMillis(1500); pool.setTestOnReturn(true); pool.setTestOnBorrow(false); // Borrow an instance and hold if for 5 seconds final WaitingTestThread thread1 = new WaitingTestThread(pool, 5000); thread1.start(); // Borrow another instance and return it after 500 ms (validation will fail) final WaitingTestThread thread2 = new WaitingTestThread(pool, 500); thread2.start(); Thread.sleep(50); // Try to borrow an object final String obj = pool.borrowObject(); pool.returnObject(obj); } } // POOL-276 @Test public void testValidationOnCreateOnly() throws Exception { genericObjectPool.setMaxTotal(1); genericObjectPool.setTestOnCreate(true); genericObjectPool.setTestOnBorrow(false); genericObjectPool.setTestOnReturn(false); genericObjectPool.setTestWhileIdle(false); final String o1 = genericObjectPool.borrowObject(); assertEquals("0", o1); final Timer t = new Timer(); t.schedule( new TimerTask() { @Override public void run() { genericObjectPool.returnObject(o1); } }, 3000); final String o2 = genericObjectPool.borrowObject(); assertEquals("0", o2); assertEquals(1, simpleFactory.validateCounter); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testWhenExhaustedBlock() throws Exception { genericObjectPool.setMaxTotal(1); genericObjectPool.setBlockWhenExhausted(true); genericObjectPool.setMaxWaitMillis(10L); final String obj1 = genericObjectPool.borrowObject(); assertNotNull(obj1); try { genericObjectPool.borrowObject(); fail("Expected NoSuchElementException"); } catch(final NoSuchElementException e) { // expected } genericObjectPool.returnObject(obj1); genericObjectPool.close(); } /** * POOL-189 * * @throws Exception May occur in some failure modes */ @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testWhenExhaustedBlockClosePool() throws Exception { genericObjectPool.setMaxTotal(1); genericObjectPool.setBlockWhenExhausted(true); genericObjectPool.setMaxWaitMillis(-1); final Object obj1 = genericObjectPool.borrowObject(); // Make sure an object was obtained assertNotNull(obj1); // Create a separate thread to try and borrow another object final WaitingTestThread wtt = new WaitingTestThread(genericObjectPool, 200); wtt.start(); // Give wtt time to start Thread.sleep(200); // close the pool (Bug POOL-189) genericObjectPool.close(); // Give interrupt time to take effect Thread.sleep(200); // Check thread was interrupted assertTrue(wtt._thrown instanceof InterruptedException); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testWhenExhaustedBlockInterrupt() throws Exception { genericObjectPool.setMaxTotal(1); genericObjectPool.setBlockWhenExhausted(true); genericObjectPool.setMaxWaitMillis(-1); final String obj1 = genericObjectPool.borrowObject(); // Make sure on object was obtained assertNotNull(obj1); // Create a separate thread to try and borrow another object final WaitingTestThread wtt = new WaitingTestThread(genericObjectPool, 200000); wtt.start(); // Give wtt time to start Thread.sleep(200); wtt.interrupt(); // Give interrupt time to take effect Thread.sleep(200); // Check thread was interrupted assertTrue(wtt._thrown instanceof InterruptedException); // Return object to the pool genericObjectPool.returnObject(obj1); // Bug POOL-162 - check there is now an object in the pool genericObjectPool.setMaxWaitMillis(10L); String obj2 = null; try { obj2 = genericObjectPool.borrowObject(); assertNotNull(obj2); } catch(final NoSuchElementException e) { // Not expected fail("NoSuchElementException not expected"); } genericObjectPool.returnObject(obj2); genericObjectPool.close(); } @Test @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) public void testWhenExhaustedFail() throws Exception { genericObjectPool.setMaxTotal(1); genericObjectPool.setBlockWhenExhausted(false); final String obj1 = genericObjectPool.borrowObject(); assertNotNull(obj1); try { genericObjectPool.borrowObject(); fail("Expected NoSuchElementException"); } catch(final NoSuchElementException e) { // expected } genericObjectPool.returnObject(obj1); assertEquals(1, genericObjectPool.getNumIdle()); genericObjectPool.close(); } } TestGenericObjectPoolClassLoaders.java000066400000000000000000000105441405425132200370450ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import java.net.URL; import java.net.URLClassLoader; import java.time.Duration; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.junit.jupiter.api.Test; public class TestGenericObjectPoolClassLoaders { private static class CustomClassLoader extends URLClassLoader { private final int n; CustomClassLoader(final int n) { super(new URL[] { BASE_URL }); this.n = n; } @Override public URL findResource(final String name) { if (!name.endsWith(String.valueOf(n))) { return null; } return super.findResource(name); } } private static class CustomClassLoaderObjectFactory extends BasePooledObjectFactory { private final int n; CustomClassLoaderObjectFactory(final int n) { this.n = n; } @Override public URL create() throws Exception { final URL url = Thread.currentThread().getContextClassLoader() .getResource("test" + n); if (url == null) { throw new IllegalStateException("Object should not be null"); } return url; } @Override public PooledObject wrap(final URL value) { return new DefaultPooledObject<>(value); } } private static final URL BASE_URL = TestGenericObjectPoolClassLoaders.class.getResource( "/org/apache/commons/pool2/impl/"); @Test public void testContextClassLoader() throws Exception { final ClassLoader savedClassloader = Thread.currentThread().getContextClassLoader(); try { final CustomClassLoader cl1 = new CustomClassLoader(1); Thread.currentThread().setContextClassLoader(cl1); final CustomClassLoaderObjectFactory factory1 = new CustomClassLoaderObjectFactory(1); final GenericObjectPool pool1 = new GenericObjectPool<>(factory1); pool1.setMinIdle(1); pool1.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); int counter = 0; while (counter < 50 && pool1.getNumIdle() != 1) { Thread.sleep(100); counter++; } assertEquals( 1, pool1.getNumIdle(),"Wrong number of idle objects in pool1"); // --------------- final CustomClassLoader cl2 = new CustomClassLoader(2); Thread.currentThread().setContextClassLoader(cl2); final CustomClassLoaderObjectFactory factory2 = new CustomClassLoaderObjectFactory(2); final GenericObjectPool pool2 = new GenericObjectPool<>(factory2); pool2.setMinIdle(1); pool2.addObject(); assertEquals( 1, pool2.getNumIdle(),"Wrong number of idle objects in pool2"); pool2.clear(); pool2.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); counter = 0; while (counter < 50 && pool2.getNumIdle() != 1) { Thread.sleep(100); counter++; } assertEquals( 1, pool2.getNumIdle(),"Wrong number of idle objects in pool2"); pool1.close(); pool2.close(); } finally { Thread.currentThread().setContextClassLoader(savedClassloader); } } } TestGenericObjectPoolFactoryCreateFailure.java000066400000000000000000000121301405425132200405220ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertFalse; import java.time.Duration; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; /** * @author Pavel Kolesov as contributed in POOL-340 */ public class TestGenericObjectPoolFactoryCreateFailure { private static class SingleObjectFactory extends BasePooledObjectFactory { private final AtomicBoolean created = new AtomicBoolean(); @Override public Object create() throws Exception { if (!created.getAndSet(true)) { return new Object(); } throw new Exception("Already created"); } @Override public boolean validateObject(final PooledObject p) { return true; } @Override public PooledObject wrap(final Object obj) { return new DefaultPooledObject<>(new Object()); } } private static class WinnerRunnable implements Runnable { private final CountDownLatch barrier; private final AtomicBoolean failed; private final GenericObjectPool pool; private WinnerRunnable(final GenericObjectPool pool, final CountDownLatch barrier, final AtomicBoolean failed) { this.pool = pool; this.failed = failed; this.barrier = barrier; } @Override public void run() { try { println("start borrowing in parallel thread"); final Object obj = pool.borrowObject(); // wait for another thread to start borrowObject if (!barrier.await(5, TimeUnit.SECONDS)) { println("Timeout waiting"); failed.set(true); } else { // just to make sure, borrowObject has started waiting on queue sleepIgnoreException(1000); } pool.returnObject(obj); println("ended borrowing in parallel thread"); } catch (final Exception e) { failed.set(true); e.printStackTrace(); } } } private static void println(final String msg) { // System.out.println(msg); } private static void sleepIgnoreException(final long millis) { try { Thread.sleep(millis); } catch(final Throwable e) { // ignore } } @Test @Timeout(value = 10_000, unit = TimeUnit.MILLISECONDS) public void testBorrowObjectStuck() { final SingleObjectFactory factory = new SingleObjectFactory(); final GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxIdle(1); config.setMaxTotal(1); config.setBlockWhenExhausted(true); config.setMinIdle(0); config.setTestOnBorrow(true); config.setTestOnReturn(true); config.setTestWhileIdle(false); config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1)); config.setMinEvictableIdleTime(Duration.ofMillis(-1)); config.setSoftMinEvictableIdleTime(Duration.ofMillis(-1)); config.setMaxWaitMillis(-1); try (GenericObjectPool pool = new GenericObjectPool<>(factory, config)) { final AtomicBoolean failed = new AtomicBoolean(); final CountDownLatch barrier = new CountDownLatch(1); final Thread thread1 = new Thread(new WinnerRunnable(pool, barrier, failed)); thread1.start(); // wait for object to be created while (!factory.created.get()) { sleepIgnoreException(5); } // now borrow barrier.countDown(); try { println("try borrow in main thread"); final Object o = pool.borrowObject(); println("Success borrow in main thread " + o); } catch (final Exception e) { e.printStackTrace(); } assertFalse(failed.get()); } } }TestLinkedBlockingDeque.java000066400000000000000000000364751405425132200350660ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; /** * Tests for {@link LinkedBlockingDeque}. */ public class TestLinkedBlockingDeque { private static final Duration TIMEOUT_50_MILLIS = Duration.ofMillis(50); private static final Integer ONE = Integer.valueOf(1); private static final Integer TWO = Integer.valueOf(2); private static final Integer THREE = Integer.valueOf(3); LinkedBlockingDeque deque; @BeforeEach public void setUp() { deque = new LinkedBlockingDeque<>(2); } @Test public void testAdd() { assertTrue(deque.add(ONE)); assertTrue(deque.add(TWO)); try { assertTrue(deque.add(THREE)); fail("Not supposed to get here"); } catch (final IllegalStateException e) {} try { assertTrue(deque.add(null)); fail("Not supposed to get here"); } catch (final NullPointerException e) {} } @Test public void testAddFirst() { deque.addFirst(ONE); deque.addFirst(TWO); assertEquals(2, deque.size()); try { deque.addFirst(THREE); fail("Not supposed to get here"); } catch (final IllegalStateException e) {} assertEquals(Integer.valueOf(2), deque.pop()); } @Test public void testAddLast() { deque.addLast(ONE); deque.addLast(TWO); assertEquals(2, deque.size()); try { deque.addLast(THREE); fail("Not supposed to get here"); } catch (final IllegalStateException e) {} assertEquals(Integer.valueOf(1), deque.pop()); } @Test public void testClear() { deque.add(ONE); deque.add(TWO); deque.clear(); deque.add(ONE); assertEquals(1, deque.size()); } @Test public void testConstructors() { LinkedBlockingDeque deque = new LinkedBlockingDeque<>(); assertEquals(Integer.MAX_VALUE, deque.remainingCapacity()); deque = new LinkedBlockingDeque<>(2); assertEquals(2, deque.remainingCapacity()); deque = new LinkedBlockingDeque<>(Arrays.asList(ONE, TWO)); assertEquals(2, deque.size()); try { deque = new LinkedBlockingDeque<>(Arrays.asList(ONE, null)); fail("Not supposed to get here"); } catch (final NullPointerException npe) { // OK } } @Test public void testContains() { deque.add(ONE); assertTrue(deque.contains(ONE)); assertFalse(deque.contains(TWO)); assertFalse(deque.contains(null)); deque.add(TWO); assertTrue(deque.contains(TWO)); assertFalse(deque.contains(THREE)); } @Test public void testDescendingIterator() { try { deque.descendingIterator().next(); fail("Not supposed to get here"); } catch (final NoSuchElementException e) {} deque.add(ONE); deque.add(TWO); final Iterator iter = deque.descendingIterator(); assertEquals(Integer.valueOf(2), iter.next()); iter.remove(); assertEquals(Integer.valueOf(1), iter.next()); } @Test public void testDrainTo() { Collection c = new ArrayList<>(); deque.add(ONE); deque.add(TWO); assertEquals(2, deque.drainTo(c)); assertEquals(2, c.size()); c = new ArrayList<>(); deque.add(ONE); deque.add(TWO); assertEquals(1, deque.drainTo(c, 1)); assertEquals(1, deque.size()); assertEquals(1, c.size()); assertEquals(Integer.valueOf(1), c.iterator().next()); } @Test public void testElement() { try { deque.element(); fail("Not supposed to get here"); } catch (final NoSuchElementException e){} deque.add(ONE); deque.add(TWO); assertEquals(Integer.valueOf(1), deque.element()); } @Test public void testGetFirst() { try { deque.getFirst(); fail("Not supposed to get here"); } catch (final NoSuchElementException e){} deque.add(ONE); deque.add(TWO); assertEquals(Integer.valueOf(1), deque.getFirst()); } @Test public void testGetLast() { try { deque.getLast(); fail("Not supposed to get here"); } catch (final NoSuchElementException e){} deque.add(ONE); deque.add(TWO); assertEquals(Integer.valueOf(2), deque.getLast()); } @Test public void testIterator() { try { deque.iterator().next(); fail("Not supposed to get here"); } catch (final NoSuchElementException e) {} deque.add(ONE); deque.add(TWO); final Iterator iter = deque.iterator(); assertEquals(Integer.valueOf(1), iter.next()); iter.remove(); assertEquals(Integer.valueOf(2), iter.next()); } @Test public void testOffer() { assertTrue(deque.offer(ONE)); assertTrue(deque.offer(TWO)); assertFalse(deque.offer(THREE)); try { deque.offer(null); fail("Not supposed to get here"); } catch (final NullPointerException e) {} } @Test public void testOfferFirst() { deque.offerFirst(ONE); deque.offerFirst(TWO); assertEquals(2, deque.size()); try { deque.offerFirst(null); fail("Not supposed to get here"); } catch (final NullPointerException e) {} assertEquals(Integer.valueOf(2), deque.pop()); } @Test public void testOfferFirstWithTimeout() throws InterruptedException { try { deque.offerFirst(null); fail("Not supposed to get here"); } catch (final NullPointerException e) {} assertTrue(deque.offerFirst(ONE, TIMEOUT_50_MILLIS)); assertTrue(deque.offerFirst(TWO, TIMEOUT_50_MILLIS)); assertFalse(deque.offerFirst(THREE, TIMEOUT_50_MILLIS)); } @Test public void testOfferLast() { deque.offerLast(ONE); deque.offerLast(TWO); assertEquals(2, deque.size()); try { deque.offerLast(null); fail("Not supposed to get here"); } catch (final NullPointerException e) {} assertEquals(Integer.valueOf(1), deque.pop()); } @Test public void testOfferLastWithTimeout() throws InterruptedException { try { deque.offerLast(null); fail("Not supposed to get here"); } catch (final NullPointerException e) {} assertTrue(deque.offerLast(ONE, TIMEOUT_50_MILLIS)); assertTrue(deque.offerLast(TWO, TIMEOUT_50_MILLIS)); assertFalse(deque.offerLast(THREE, TIMEOUT_50_MILLIS)); } @Test public void testOfferWithTimeout() throws InterruptedException { assertTrue(deque.offer(ONE, TIMEOUT_50_MILLIS)); assertTrue(deque.offer(TWO, TIMEOUT_50_MILLIS)); assertFalse(deque.offer(THREE, TIMEOUT_50_MILLIS)); try { deque.offer(null, TIMEOUT_50_MILLIS); fail("Not supposed to get here"); } catch (final NullPointerException e) {} } @Test public void testPeek() { assertNull(deque.peek()); deque.add(ONE); deque.add(TWO); assertEquals(Integer.valueOf(1), deque.peek()); } @Test public void testPeekFirst() { assertNull(deque.peekFirst()); deque.add(ONE); deque.add(TWO); assertEquals(Integer.valueOf(1), deque.peekFirst()); } @Test public void testPeekLast() { assertNull(deque.peekLast()); deque.add(ONE); deque.add(TWO); assertEquals(Integer.valueOf(2), deque.peekLast()); } @Test public void testPollFirst() { assertNull(deque.pollFirst()); assertTrue(deque.offerFirst(ONE)); assertTrue(deque.offerFirst(TWO)); assertEquals(Integer.valueOf(2), deque.pollFirst()); } @Test public void testPollFirstWithTimeout() throws InterruptedException { assertNull(deque.pollFirst()); assertNull(deque.pollFirst(TIMEOUT_50_MILLIS)); } @Test public void testPollLast() { assertNull(deque.pollLast()); assertTrue(deque.offerFirst(ONE)); assertTrue(deque.offerFirst(TWO)); assertEquals(Integer.valueOf(1), deque.pollLast()); } @Test public void testPollLastWithTimeout() throws InterruptedException { assertNull(deque.pollLast()); assertNull(deque.pollLast(TIMEOUT_50_MILLIS)); } @Test public void testPollWithTimeout() throws InterruptedException { assertNull(deque.poll(TIMEOUT_50_MILLIS)); assertNull(deque.poll(TIMEOUT_50_MILLIS)); } @Test public void testPop() { try { deque.pop(); fail("Not supposed to get here"); } catch (final NoSuchElementException e) {} deque.add(ONE); deque.add(TWO); assertEquals(Integer.valueOf(1), deque.pop()); try { deque.pop(); deque.pop(); fail("Not supposed to get here"); } catch (final NoSuchElementException e) {} } /* * https://issues.apache.org/jira/browse/POOL-281 * * Should complete almost instantly when the issue is fixed. */ @Test @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS) public void testPossibleBug() { deque = new LinkedBlockingDeque<>(); for (int i = 0; i < 3; i++) { deque.add(Integer.valueOf(i)); } // This particular sequence of method calls() (there may be others) // creates an internal state that triggers an infinite loop in the // iterator. final Iterator iter = deque.iterator(); iter.next(); deque.remove(Integer.valueOf(1)); deque.remove(Integer.valueOf(0)); deque.remove(Integer.valueOf(2)); iter.next(); } @Test public void testPush() { deque.push(ONE); deque.push(TWO); assertEquals(2, deque.size()); try { deque.push(THREE); fail("Not supposed to get here"); } catch (final IllegalStateException e) {} assertEquals(Integer.valueOf(2), deque.pop()); } @Test public void testPut() throws InterruptedException { try { deque.put(null); fail("Not supposed to get here"); } catch (final NullPointerException e) {} deque.put(ONE); deque.put(TWO); } @Test public void testPutFirst() throws InterruptedException { try { deque.putFirst(null); fail("Not supposed to get here"); } catch (final NullPointerException e) {} deque.putFirst(ONE); deque.putFirst(TWO); assertEquals(2, deque.size()); assertEquals(Integer.valueOf(2), deque.pop()); } @Test public void testPutLast() throws InterruptedException { try { deque.putLast(null); fail("Not supposed to get here"); } catch (final NullPointerException e) {} deque.putLast(ONE); deque.putLast(TWO); assertEquals(2, deque.size()); assertEquals(Integer.valueOf(1), deque.pop()); } @Test public void testRemove() { try { deque.remove(); fail("Not supposed to get here"); } catch (final NoSuchElementException e) {} deque.add(ONE); deque.add(TWO); assertEquals(Integer.valueOf(1), deque.remove()); } @Test public void testRemoveFirst() { try { deque.removeFirst(); fail("Not supposed to get here"); } catch (final NoSuchElementException e) {} deque.add(ONE); deque.add(TWO); assertEquals(Integer.valueOf(1), deque.removeFirst()); try { deque.removeFirst(); deque.removeFirst(); fail("Not supposed to get here"); } catch (final NoSuchElementException e) {} } @Test public void testRemoveLast() { try { deque.removeLast(); fail("Not supposed to get here"); } catch (final NoSuchElementException e) {} deque.add(ONE); deque.add(TWO); assertEquals(Integer.valueOf(2), deque.removeLast()); try { deque.removeLast(); deque.removeLast(); fail("Not supposed to get here"); } catch (final NoSuchElementException e) {} } @Test public void testRemoveLastOccurrence() { assertFalse(deque.removeLastOccurrence(null)); assertFalse(deque.removeLastOccurrence(ONE)); deque.add(ONE); deque.add(ONE); assertTrue(deque.removeLastOccurrence(ONE)); assertEquals(1, deque.size()); } @Test public void testTake() throws InterruptedException { assertTrue(deque.offerFirst(ONE)); assertTrue(deque.offerFirst(TWO)); assertEquals(Integer.valueOf(2), deque.take()); } @Test public void testTakeFirst() throws InterruptedException { assertTrue(deque.offerFirst(ONE)); assertTrue(deque.offerFirst(TWO)); assertEquals(Integer.valueOf(2), deque.takeFirst()); } @Test public void testTakeLast() throws InterruptedException { assertTrue(deque.offerFirst(ONE)); assertTrue(deque.offerFirst(TWO)); assertEquals(Integer.valueOf(1), deque.takeLast()); } @Test public void testToArray() { deque.add(ONE); deque.add(TWO); Object[] arr = deque.toArray(); assertEquals(Integer.valueOf(1), arr[0]); assertEquals(Integer.valueOf(2), arr[1]); arr = deque.toArray(new Integer[0]); assertEquals(Integer.valueOf(1), arr[0]); assertEquals(Integer.valueOf(2), arr[1]); arr = deque.toArray(new Integer[0]); assertEquals(Integer.valueOf(1), arr[0]); assertEquals(Integer.valueOf(2), arr[1]); } } TestPoolImplUtils.java000066400000000000000000000051631405425132200337650ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.junit.jupiter.api.Test; public class TestPoolImplUtils { @SuppressWarnings("unused") private abstract static class FactoryAB extends BasePooledObjectFactory { // empty by design } private abstract static class FactoryBA extends FactoryAB { // empty by design } private abstract static class FactoryC extends FactoryBA { // empty by design } @SuppressWarnings("unused") private abstract static class FactoryDE extends FactoryC{ // empty by design } private abstract static class FactoryF extends FactoryDE{ // empty by design } private static class NotSimpleFactory extends FactoryF { @Override public Long create() throws Exception { return null; } @Override public PooledObject wrap(final Long obj) { return null; } } private static class SimpleFactory extends BasePooledObjectFactory { @Override public String create() throws Exception { return null; } @Override public PooledObject wrap(final String obj) { return null; } } @Test public void testFactoryTypeNotSimple() { final Class result = PoolImplUtils.getFactoryType(NotSimpleFactory.class); assertEquals(Long.class, result); } @Test public void testFactoryTypeSimple() { final Class result = PoolImplUtils.getFactoryType(SimpleFactory.class); assertEquals(String.class, result); } } TestPooledSoftReference.java000066400000000000000000000040451405425132200351040ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import java.lang.ref.SoftReference; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests for PooledSoftReference. */ public class TestPooledSoftReference { private static final String REFERENT = "test"; private static final String REFERENT2 = "test2"; PooledSoftReference ref; @BeforeEach public void setUp() { final SoftReference softRef = new SoftReference<>(REFERENT); ref = new PooledSoftReference<>(softRef); } @Test public void testPooledSoftReference() { assertEquals(REFERENT, ref.getObject()); SoftReference softRef = ref.getReference(); assertEquals(REFERENT, softRef.get()); softRef.clear(); softRef = new SoftReference<>(REFERENT2); ref.setReference(softRef); assertEquals(REFERENT2, ref.getObject()); softRef = ref.getReference(); assertEquals(REFERENT2, softRef.get()); softRef.clear(); } @Test public void testToString() { final String expected = "Referenced Object: test, State: IDLE"; assertEquals(expected, ref.toString()); } } TestSoftRefOutOfMemory.java000066400000000000000000000215721405425132200347310ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; /** */ public class TestSoftRefOutOfMemory { public static class LargePoolableObjectFactory extends BasePooledObjectFactory { private final String buffer; private int counter = 0; public LargePoolableObjectFactory(final int size) { final char[] data = new char[size]; Arrays.fill(data, '.'); buffer = new String(data); } @Override public String create() { counter++; return String.valueOf(counter) + buffer; } @Override public PooledObject wrap(final String value) { return new DefaultPooledObject<>(value); } } private static class OomeFactory extends BasePooledObjectFactory { private final OomeTrigger trigger; public OomeFactory(final OomeTrigger trigger) { this.trigger = trigger; } @Override public String create() throws Exception { if (trigger.equals(OomeTrigger.CREATE)) { throw new OutOfMemoryError(); } // It seems that as of Java 1.4 String.valueOf may return an // intern()'ed String this may cause problems when the tests // depend on the returned object to be eventually garbaged // collected. Either way, making sure a new String instance // is returned eliminated false failures. return new String(); } @Override public void destroyObject(final PooledObject p) throws Exception { if (trigger.equals(OomeTrigger.DESTROY)) { throw new OutOfMemoryError(); } super.destroyObject(p); } @Override public boolean validateObject(final PooledObject p) { if (trigger.equals(OomeTrigger.VALIDATE)) { throw new OutOfMemoryError(); } return !trigger.equals(OomeTrigger.DESTROY); } @Override public PooledObject wrap(final String value) { return new DefaultPooledObject<>(value); } } private enum OomeTrigger { CREATE, VALIDATE, DESTROY } public static class SmallPoolableObjectFactory extends BasePooledObjectFactory { private int counter = 0; @Override public String create() { counter++; // It seems that as of Java 1.4 String.valueOf may return an // intern()'ed String this may cause problems when the tests // depend on the returned object to be eventually garbaged // collected. Either way, making sure a new String instance // is returned eliminated false failures. return new String(String.valueOf(counter)); } @Override public PooledObject wrap(final String value) { return new DefaultPooledObject<>(value); } } private SoftReferenceObjectPool pool; @AfterEach public void tearDown() throws Exception { if (pool != null) { pool.close(); pool = null; } System.gc(); } @Test public void testOutOfMemory() throws Exception { pool = new SoftReferenceObjectPool<>(new SmallPoolableObjectFactory()); String obj = pool.borrowObject(); assertEquals("1", obj); pool.returnObject(obj); obj = null; assertEquals(1, pool.getNumIdle()); final List garbage = new LinkedList<>(); final Runtime runtime = Runtime.getRuntime(); while (pool.getNumIdle() > 0) { try { long freeMemory = runtime.freeMemory(); if (freeMemory > Integer.MAX_VALUE) { freeMemory = Integer.MAX_VALUE; } garbage.add(new byte[Math.min(1024 * 1024, (int) freeMemory / 2)]); } catch (final OutOfMemoryError oome) { System.gc(); } System.gc(); } garbage.clear(); System.gc(); obj = pool.borrowObject(); assertEquals("2", obj); pool.returnObject(obj); obj = null; assertEquals(1, pool.getNumIdle()); } @Test public void testOutOfMemory1000() throws Exception { pool = new SoftReferenceObjectPool<>(new SmallPoolableObjectFactory()); for (int i = 0 ; i < 1000 ; i++) { pool.addObject(); } String obj = pool.borrowObject(); assertEquals("1000", obj); pool.returnObject(obj); obj = null; assertEquals(1000, pool.getNumIdle()); final List garbage = new LinkedList<>(); final Runtime runtime = Runtime.getRuntime(); while (pool.getNumIdle() > 0) { try { long freeMemory = runtime.freeMemory(); if (freeMemory > Integer.MAX_VALUE) { freeMemory = Integer.MAX_VALUE; } garbage.add(new byte[Math.min(1024 * 1024, (int) freeMemory / 2)]); } catch (final OutOfMemoryError oome) { System.gc(); } System.gc(); } garbage.clear(); System.gc(); obj = pool.borrowObject(); assertEquals("1001", obj); pool.returnObject(obj); obj = null; assertEquals(1, pool.getNumIdle()); } /** * Makes sure an {@link OutOfMemoryError} isn't swallowed. * * @throws Exception May occur in some failure modes */ @Test public void testOutOfMemoryError() throws Exception { pool = new SoftReferenceObjectPool<>( new OomeFactory(OomeTrigger.CREATE)); try { pool.borrowObject(); fail("Expected out of memory."); } catch (final OutOfMemoryError ex) { // expected } pool.close(); pool = new SoftReferenceObjectPool<>( new OomeFactory(OomeTrigger.VALIDATE)); try { pool.borrowObject(); fail("Expected out of memory."); } catch (final OutOfMemoryError ex) { // expected } pool.close(); pool = new SoftReferenceObjectPool<>( new OomeFactory(OomeTrigger.DESTROY)); try { pool.borrowObject(); fail("Expected out of memory."); } catch (final OutOfMemoryError ex) { // expected } pool.close(); } @Test public void testOutOfMemoryLarge() throws Exception { pool = new SoftReferenceObjectPool<>(new LargePoolableObjectFactory(1000000)); String obj = pool.borrowObject(); assertTrue(obj.startsWith("1.")); pool.returnObject(obj); obj = null; assertEquals(1, pool.getNumIdle()); final List garbage = new LinkedList<>(); final Runtime runtime = Runtime.getRuntime(); while (pool.getNumIdle() > 0) { try { long freeMemory = runtime.freeMemory(); if (freeMemory > Integer.MAX_VALUE) { freeMemory = Integer.MAX_VALUE; } garbage.add(new byte[Math.min(1024 * 1024, (int) freeMemory / 2)]); } catch (final OutOfMemoryError oome) { System.gc(); } System.gc(); } garbage.clear(); System.gc(); obj = pool.borrowObject(); assertTrue(obj.startsWith("2.")); pool.returnObject(obj); obj = null; assertEquals(1, pool.getNumIdle()); } }TestSoftReferenceObjectPool.java000066400000000000000000000041061405425132200357200ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.TestBaseObjectPool; /** */ public class TestSoftReferenceObjectPool extends TestBaseObjectPool { private static class SimpleFactory extends BasePooledObjectFactory { int counter = 0; @Override public String create() { return String.valueOf(counter++); } @Override public PooledObject wrap(final String value) { return new DefaultPooledObject<>(value); } } @Override protected Object getNthObject(final int n) { return String.valueOf(n); } @Override protected boolean isFifo() { return false; } @Override protected boolean isLifo() { return false; } @Override protected ObjectPool makeEmptyPool(final int cap) { return new SoftReferenceObjectPool<>(new SimpleFactory()); } @Override protected ObjectPool makeEmptyPool(final PooledObjectFactory factory) { return new SoftReferenceObjectPool<>(factory); } } TestSynchronizedPooledObjectFactory.java000066400000000000000000000066701405425132200375160ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/impl/* * 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.commons.pool2.impl; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; /** * Copies PoolUtil's private static class SynchronizedPooledObjectFactory. * * A fully synchronized PooledObjectFactory that wraps a PooledObjectFactory and * synchronizes access to the wrapped factory methods. *

* Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons Pool * library. *

*/ final class TestSynchronizedPooledObjectFactory implements PooledObjectFactory { /** Synchronization lock */ private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); /** Wrapped factory */ private final PooledObjectFactory factory; /** * Create a SynchronizedPoolableObjectFactory wrapping the given factory. * * @param factory * underlying factory to wrap * @throws IllegalArgumentException * if the factory is null */ TestSynchronizedPooledObjectFactory(final PooledObjectFactory factory) throws IllegalArgumentException { if (factory == null) { throw new IllegalArgumentException("factory must not be null."); } this.factory = factory; } /** * {@inheritDoc} */ @Override public void activateObject(final PooledObject p) throws Exception { writeLock.lock(); try { factory.activateObject(p); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void destroyObject(final PooledObject p) throws Exception { writeLock.lock(); try { factory.destroyObject(p); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public PooledObject makeObject() throws Exception { writeLock.lock(); try { return factory.makeObject(); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public void passivateObject(final PooledObject p) throws Exception { writeLock.lock(); try { factory.passivateObject(p); } finally { writeLock.unlock(); } } /** * {@inheritDoc} */ @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("SynchronizedPoolableObjectFactory"); sb.append("{factory=").append(factory); sb.append('}'); return sb.toString(); } /** * {@inheritDoc} */ @Override public boolean validateObject(final PooledObject p) { writeLock.lock(); try { return factory.validateObject(p); } finally { writeLock.unlock(); } } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/performance/000077500000000000000000000000001405425132200311205ustar00rootroot00000000000000PerformanceTest.java000066400000000000000000000170511405425132200350110ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/performance/* * 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.commons.pool2.performance; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.commons.pool2.impl.GenericObjectPool; /** * Multi-thread performance test * */ public class PerformanceTest { class PerfTask implements Callable { final TaskStats taskStats = new TaskStats(); long borrowTimeMillis; long returnTimeMillis; @Override public TaskStats call() throws Exception { runOnce(); // warmup for (int i = 0; i < nrIterations; i++) { runOnce(); taskStats.totalBorrowTime += borrowTimeMillis; taskStats.totalReturnTime += returnTimeMillis; taskStats.nrSamples++; if (logLevel >= 2) { final String name = "thread" + Thread.currentThread().getName(); System.out.println("result " + taskStats.nrSamples + '\t' + name + '\t' + "borrow time: " + borrowTimeMillis + '\t' + "return time: " + returnTimeMillis + '\t' + "waiting: " + taskStats.waiting + '\t' + "complete: " + taskStats.complete); } } return taskStats; } public void runOnce() { try { taskStats.waiting++; if (logLevel >= 5) { final String name = "thread" + Thread.currentThread().getName(); System.out.println(name + " waiting: " + taskStats.waiting + " complete: " + taskStats.complete); } final long bbeginMillis = System.currentTimeMillis(); final Integer o = pool.borrowObject(); final long bendMillis = System.currentTimeMillis(); taskStats.waiting--; if (logLevel >= 3) { final String name = "thread" + Thread.currentThread().getName(); System.out.println(name + " waiting: " + taskStats.waiting + " complete: " + taskStats.complete); } final long rbeginMillis = System.currentTimeMillis(); pool.returnObject(o); final long rendMillis = System.currentTimeMillis(); Thread.yield(); taskStats.complete++; borrowTimeMillis = bendMillis - bbeginMillis; returnTimeMillis = rendMillis - rbeginMillis; } catch (final Exception e) { e.printStackTrace(); } } } private static class TaskStats { public int waiting = 0; public int complete = 0; public long totalBorrowTime = 0; public long totalReturnTime = 0; public int nrSamples = 0; } public static void main(final String[] args) { final PerformanceTest test = new PerformanceTest(); test.setLogLevel(0); System.out.println("Increase threads"); test.run(1, 50, 5, 5); test.run(1, 100, 5, 5); test.run(1, 200, 5, 5); test.run(1, 400, 5, 5); System.out.println("Increase threads & poolSize"); test.run(1, 50, 5, 5); test.run(1, 100, 10, 10); test.run(1, 200, 20, 20); test.run(1, 400, 40, 40); System.out.println("Increase maxIdle"); test.run(1, 400, 40, 5); test.run(1, 400, 40, 40); // System.out.println("Show creation/destruction of objects"); // test.setLogLevel(4); // test.run(1, 400, 40, 5); } private int logLevel = 0; private int nrIterations = 5; private GenericObjectPool pool; private void run(final int iterations, final int nrThreads, final int maxTotal, final int maxIdle) { this.nrIterations = iterations; final SleepingObjectFactory factory = new SleepingObjectFactory(); if (logLevel >= 4) { factory.setDebug(true); } pool = new GenericObjectPool<>(factory); pool.setMaxTotal(maxTotal); pool.setMaxIdle(maxIdle); pool.setTestOnBorrow(true); final ExecutorService threadPool = Executors.newFixedThreadPool(nrThreads); final List> tasks = new ArrayList<>(); for (int i = 0; i < nrThreads; i++) { tasks.add(new PerfTask()); Thread.yield(); } if (logLevel >= 1) { System.out.println("created"); } Thread.yield(); List> futures = null; try { futures = threadPool.invokeAll(tasks); } catch (final InterruptedException e) { e.printStackTrace(); } if (logLevel >= 1) { System.out.println("started"); } Thread.yield(); if (logLevel >= 1) { System.out.println("go"); } Thread.yield(); if (logLevel >= 1) { System.out.println("finish"); } final TaskStats aggregate = new TaskStats(); if (futures != null) { for (final Future future : futures) { TaskStats taskStats = null; try { taskStats = future.get(); } catch (final InterruptedException | ExecutionException e) { e.printStackTrace(); } if (taskStats != null) { aggregate.complete += taskStats.complete; aggregate.nrSamples += taskStats.nrSamples; aggregate.totalBorrowTime += taskStats.totalBorrowTime; aggregate.totalReturnTime += taskStats.totalReturnTime; aggregate.waiting += taskStats.waiting; } } } System.out.println("-----------------------------------------"); System.out.println("nrIterations: " + iterations); System.out.println("nrThreads: " + nrThreads); System.out.println("maxTotal: " + maxTotal); System.out.println("maxIdle: " + maxIdle); System.out.println("nrSamples: " + aggregate.nrSamples); System.out.println("totalBorrowTime: " + aggregate.totalBorrowTime); System.out.println("totalReturnTime: " + aggregate.totalReturnTime); System.out.println("avg BorrowTime: " + aggregate.totalBorrowTime / aggregate.nrSamples); System.out.println("avg ReturnTime: " + aggregate.totalReturnTime / aggregate.nrSamples); threadPool.shutdown(); } public void setLogLevel(final int i) { logLevel = i; } } SleepingObjectFactory.java000066400000000000000000000053431405425132200361360ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/performance/* * 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.commons.pool2.performance; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.DefaultPooledObject; /** * Sleepy ObjectFactory (everything takes a while longer) */ public class SleepingObjectFactory implements PooledObjectFactory { private int counter; private boolean debug; @Override public void activateObject(final PooledObject obj) throws Exception { debug("activateObject", obj); sleep(10); } private void debug(final String method, final Object obj) { if (debug) { final String thread = "thread" + Thread.currentThread().getName(); System.out.println(thread + ": " + method + " " + obj); } } @Override public void destroyObject(final PooledObject obj) throws Exception { debug("destroyObject", obj); sleep(250); } public boolean isDebug() { return debug; } @Override public PooledObject makeObject() throws Exception { // Deliberate choice to create a new object in case future unit tests // check for a specific object. final Integer obj = Integer.valueOf(counter++); debug("makeObject", obj); sleep(500); return new DefaultPooledObject<>(obj); } @Override public void passivateObject(final PooledObject obj) throws Exception { debug("passivateObject", obj); sleep(10); } public void setDebug(final boolean b) { debug = b; } private void sleep(final long millis) { try { Thread.sleep(millis); } catch (final InterruptedException e) { // ignore } } @Override public boolean validateObject(final PooledObject obj) { debug("validateObject", obj); sleep(30); return true; } } commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/proxy/000077500000000000000000000000001405425132200300005ustar00rootroot00000000000000BaseTestProxiedKeyedObjectPool.java000066400000000000000000000144621405425132200366030ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.PrintWriter; import java.io.StringWriter; import java.time.Duration; import org.apache.commons.pool2.BaseKeyedPooledObjectFactory; import org.apache.commons.pool2.KeyedObjectPool; import org.apache.commons.pool2.KeyedPooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.impl.AbandonedConfig; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericKeyedObjectPool; import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public abstract class BaseTestProxiedKeyedObjectPool { private static class TestKeyedObjectFactory extends BaseKeyedPooledObjectFactory { @Override public TestObject create(final String key) throws Exception { return new TestObjectImpl(); } @Override public PooledObject wrap(final TestObject value) { return new DefaultPooledObject<>(value); } } protected interface TestObject { String getData(); void setData(String data); } private static class TestObjectImpl implements TestObject { private String data; @Override public String getData() { return data; } @Override public void setData(final String data) { this.data = data; } } private static final String KEY1 = "key1"; private static final String DATA1 = "data1"; private static final Duration ABANDONED_TIMEOUT_SECS = Duration.ofSeconds(3); private KeyedObjectPool pool; private StringWriter log = null; protected abstract ProxySource getproxySource(); @BeforeEach public void setUp() { log = new StringWriter(); final PrintWriter pw = new PrintWriter(log); final AbandonedConfig abandonedConfig = new AbandonedConfig(); abandonedConfig.setLogAbandoned(true); abandonedConfig.setRemoveAbandonedOnBorrow(true); abandonedConfig.setUseUsageTracking(true); abandonedConfig.setRemoveAbandonedTimeout(ABANDONED_TIMEOUT_SECS); abandonedConfig.setLogWriter(pw); final GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig<>(); config.setMaxTotal(3); final KeyedPooledObjectFactory factory = new TestKeyedObjectFactory(); @SuppressWarnings("resource") final KeyedObjectPool innerPool = new GenericKeyedObjectPool<>( factory, config, abandonedConfig); pool = new ProxiedKeyedObjectPool<>(innerPool, getproxySource()); } @Test public void testAccessAfterInvalidate() throws Exception { final TestObject obj = pool.borrowObject(KEY1); assertNotNull(obj); // Make sure proxied methods are working obj.setData(DATA1); assertEquals(DATA1, obj.getData()); pool.invalidateObject(KEY1, obj); assertNotNull(obj); assertThrows(IllegalStateException.class, obj::getData); } @Test public void testAccessAfterReturn() throws Exception { final TestObject obj = pool.borrowObject(KEY1); assertNotNull(obj); // Make sure proxied methods are working obj.setData(DATA1); assertEquals(DATA1, obj.getData()); pool.returnObject(KEY1, obj); assertNotNull(obj); assertThrows(IllegalStateException.class, obj::getData); } @Test public void testBorrowObject() throws Exception { final TestObject obj = pool.borrowObject(KEY1); assertNotNull(obj); // Make sure proxied methods are working obj.setData(DATA1); assertEquals(DATA1, obj.getData()); pool.returnObject(KEY1, obj); } @Test public void testPassThroughMethods01() throws Exception { assertEquals(0, pool.getNumActive()); assertEquals(0, pool.getNumIdle()); pool.addObject(KEY1); assertEquals(0, pool.getNumActive()); assertEquals(1, pool.getNumIdle()); pool.clear(); assertEquals(0, pool.getNumActive()); assertEquals(0, pool.getNumIdle()); } @Test public void testPassThroughMethods02() throws Exception { pool.close(); assertThrows(IllegalStateException.class, () -> pool.addObject(KEY1)); } @Test public void testUsageTracking() throws Exception { final TestObject obj = pool.borrowObject(KEY1); assertNotNull(obj); // Use the object to trigger collection of last used stack trace obj.setData(DATA1); // Sleep long enough for the object to be considered abandoned Thread.sleep(ABANDONED_TIMEOUT_SECS.plusSeconds(2).toMillis()); // Borrow another object to trigger the abandoned object processing pool.borrowObject(KEY1); final String logOutput = log.getBuffer().toString(); assertTrue(logOutput.contains("Pooled object created")); assertTrue(logOutput.contains("The last code to use this object was")); } } BaseTestProxiedObjectPool.java000066400000000000000000000140651405425132200356200ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.PrintWriter; import java.io.StringWriter; import java.time.Duration; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.AbandonedConfig; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public abstract class BaseTestProxiedObjectPool { protected interface TestObject { String getData(); void setData(String data); } private static class TestObjectFactory extends BasePooledObjectFactory { @Override public TestObject create() throws Exception { return new TestObjectImpl(); } @Override public PooledObject wrap(final TestObject value) { return new DefaultPooledObject<>(value); } } private static class TestObjectImpl implements TestObject { private String data; @Override public String getData() { return data; } @Override public void setData(final String data) { this.data = data; } } private static final String DATA1 = "data1"; private static final Duration ABANDONED_TIMEOUT_SECS = Duration.ofSeconds(3); private ObjectPool pool = null; private StringWriter log = null; protected abstract ProxySource getproxySource(); @BeforeEach public void setUp() { log = new StringWriter(); final PrintWriter pw = new PrintWriter(log); final AbandonedConfig abandonedConfig = new AbandonedConfig(); abandonedConfig.setLogAbandoned(true); abandonedConfig.setRemoveAbandonedOnBorrow(true); abandonedConfig.setUseUsageTracking(true); abandonedConfig.setRemoveAbandonedTimeout(ABANDONED_TIMEOUT_SECS); abandonedConfig.setLogWriter(pw); final GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(3); final PooledObjectFactory factory = new TestObjectFactory(); @SuppressWarnings("resource") final ObjectPool innerPool = new GenericObjectPool<>(factory, config, abandonedConfig); pool = new ProxiedObjectPool<>(innerPool, getproxySource()); } @Test public void testAccessAfterInvalidate() throws Exception { final TestObject obj = pool.borrowObject(); assertNotNull(obj); // Make sure proxied methods are working obj.setData(DATA1); assertEquals(DATA1, obj.getData()); pool.invalidateObject(obj); assertNotNull(obj); assertThrows(IllegalStateException.class, obj::getData); } @Test public void testAccessAfterReturn() throws Exception { final TestObject obj = pool.borrowObject(); assertNotNull(obj); // Make sure proxied methods are working obj.setData(DATA1); assertEquals(DATA1, obj.getData()); pool.returnObject(obj); assertNotNull(obj); assertThrows(IllegalStateException.class, obj::getData); } @Test public void testBorrowObject() throws Exception { final TestObject obj = pool.borrowObject(); assertNotNull(obj); // Make sure proxied methods are working obj.setData(DATA1); assertEquals(DATA1, obj.getData()); pool.returnObject(obj); } @Test public void testPassThroughMethods01() throws Exception { assertEquals(0, pool.getNumActive()); assertEquals(0, pool.getNumIdle()); pool.addObject(); assertEquals(0, pool.getNumActive()); assertEquals(1, pool.getNumIdle()); pool.clear(); assertEquals(0, pool.getNumActive()); assertEquals(0, pool.getNumIdle()); } @Test public void testPassThroughMethods02() throws Exception { pool.close(); assertThrows(IllegalStateException.class, () -> pool.addObject()); } @Test public void testUsageTracking() throws Exception { final TestObject obj = pool.borrowObject(); assertNotNull(obj); // Use the object to trigger collection of last used stack trace obj.setData(DATA1); // Sleep long enough for the object to be considered abandoned Thread.sleep(ABANDONED_TIMEOUT_SECS.plusSeconds(2).toMillis()); // Borrow another object to trigger the abandoned object processing pool.borrowObject(); final String logOutput = log.getBuffer().toString(); assertTrue(logOutput.contains("Pooled object created")); assertTrue(logOutput.contains("The last code to use this object was")); } } TestProxiedKeyedObjectPoolWithCglibProxy.java000066400000000000000000000020741405425132200406430ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; public class TestProxiedKeyedObjectPoolWithCglibProxy extends BaseTestProxiedKeyedObjectPool { @Override protected ProxySource getproxySource() { return new CglibProxySource<>(TestObject.class); } } TestProxiedKeyedObjectPoolWithJdkProxy.java000066400000000000000000000021751405425132200403350ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; public class TestProxiedKeyedObjectPoolWithJdkProxy extends BaseTestProxiedKeyedObjectPool { @Override protected ProxySource getproxySource() { return new JdkProxySource<>(this.getClass().getClassLoader(), new Class[] { TestObject.class }); } } TestProxiedObjectPoolWithCglibProxy.java000066400000000000000000000020621405425132200376560ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; public class TestProxiedObjectPoolWithCglibProxy extends BaseTestProxiedObjectPool { @Override protected ProxySource getproxySource() { return new CglibProxySource<>(TestObject.class); } } TestProxiedObjectPoolWithJdkProxy.java000066400000000000000000000021631405425132200373500ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/java/org/apache/commons/pool2/proxy/* * 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.commons.pool2.proxy; public class TestProxiedObjectPoolWithJdkProxy extends BaseTestProxiedObjectPool { @Override protected ProxySource getproxySource() { return new JdkProxySource<>(this.getClass().getClassLoader(), new Class[] { TestObject.class }); } } commons-pool-rel-commons-pool-2.10.0/src/test/resources/000077500000000000000000000000001405425132200231725ustar00rootroot00000000000000commons-pool-rel-commons-pool-2.10.0/src/test/resources/test1000066400000000000000000000000151405425132200241510ustar00rootroot00000000000000Hello world !commons-pool-rel-commons-pool-2.10.0/src/test/resources/test2000066400000000000000000000000151405425132200241520ustar00rootroot00000000000000Hello world !