From 974b67dd4021e6e839eaad25366bffe6d7a414c8 Mon Sep 17 00:00:00 2001 From: Dan Timoney Date: Fri, 31 Mar 2017 15:03:13 -0400 Subject: [PATCH] [SDNC-5] Rebase sdnc-core Upgrade to OpenDaylight Boron release, and sync changes made since 16.10 release to ONAP SDN-C distribution Change-Id: I20bef9e6d0008c4436b5624ce839bbb70ecc20a5 Signed-off-by: Dan Timoney --- .gitignore | 2 + .scanignore | 5 + dblib/common/LICENSE | 201 ++ dblib/common/NOTICE | 6 + dblib/common/doc/changelog.xml | 133 ++ dblib/common/doc/jdbc-pool.xml | 988 +++++++++ dblib/common/doc/package.xsl | 249 +++ dblib/common/doc/project.xml | 31 + dblib/common/pom.xml | 88 + .../jdbc/naming/GenericNamingResourcesFactory.java | 254 +++ .../apache/tomcat/jdbc/pool/ClassLoaderUtil.java | 81 + .../apache/tomcat/jdbc/pool/ConnectionPool.java | 1500 +++++++++++++ .../org/apache/tomcat/jdbc/pool/DataSource.java | 180 ++ .../apache/tomcat/jdbc/pool/DataSourceFactory.java | 612 ++++++ .../apache/tomcat/jdbc/pool/DataSourceProxy.java | 1496 +++++++++++++ .../jdbc/pool/DisposableConnectionFacade.java | 118 ++ .../apache/tomcat/jdbc/pool/FairBlockingQueue.java | 579 ++++++ .../apache/tomcat/jdbc/pool/JdbcInterceptor.java | 259 +++ .../jdbc/pool/MultiLockFairBlockingQueue.java | 584 ++++++ .../apache/tomcat/jdbc/pool/PoolConfiguration.java | 917 ++++++++ .../tomcat/jdbc/pool/PoolExhaustedException.java | 76 + .../apache/tomcat/jdbc/pool/PoolProperties.java | 1332 ++++++++++++ .../org/apache/tomcat/jdbc/pool/PoolUtilities.java | 58 + .../apache/tomcat/jdbc/pool/PooledConnection.java | 795 +++++++ .../apache/tomcat/jdbc/pool/ProxyConnection.java | 176 ++ .../org/apache/tomcat/jdbc/pool/TrapException.java | 101 + .../org/apache/tomcat/jdbc/pool/Validator.java | 57 + .../org/apache/tomcat/jdbc/pool/XADataSource.java | 57 + .../AbstractCreateStatementInterceptor.java | 158 ++ .../jdbc/pool/interceptor/AbstractQueryReport.java | 285 +++ .../jdbc/pool/interceptor/ConnectionState.java | 184 ++ .../pool/interceptor/QueryTimeoutInterceptor.java | 77 + .../jdbc/pool/interceptor/ResetAbandonedTimer.java | 112 + .../jdbc/pool/interceptor/SlowQueryReport.java | 516 +++++ .../jdbc/pool/interceptor/SlowQueryReportJmx.java | 338 +++ .../pool/interceptor/SlowQueryReportJmxMBean.java | 43 + .../jdbc/pool/interceptor/StatementCache.java | 358 ++++ .../interceptor/StatementDecoratorInterceptor.java | 342 +++ .../jdbc/pool/interceptor/StatementFinalizer.java | 136 ++ .../jdbc/pool/interceptor/mbeans-descriptors.xml | 56 + .../tomcat/jdbc/pool/jmx/ConnectionPool.java | 960 +++++++++ .../tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java | 107 + .../apache/tomcat/jdbc/pool/mbeans-descriptors.xml | 420 ++++ .../sdnc/sli/resource/common/CommonActivator.java | 40 + dblib/features/src/main/resources/features.xml | 1 + dblib/pom.xml | 1 + dblib/provider/pom.xml | 10 +- .../resource/dblib/CachedDataSourceFactory.java | 8 +- .../sdnc/sli/resource/dblib/DBResourceManager.java | 20 +- .../sli/resource/dblib/config/DbConfigPool.java | 4 - .../factory/AbstractDBResourceManagerFactory.java | 9 +- .../factory/AbstractResourceManagerFactory.java | 11 +- .../resource/dblib/factory/DBConfigFactory.java | 14 +- .../dblib/jdbc/JdbcDBCachedDataSource.java | 252 +++ .../dblib/jndi/JNDIDbResourceManagerFactory.java | 167 -- .../resource/dblib/jndi/JndiCachedDataSource.java | 131 -- dblib/provider/src/main/resources/dblib.properties | 2 +- filters/provider/pom.xml | 6 +- .../java/org/openecomp/sdnc/filters/LogFilter.java | 2 +- .../filters/RequestResponseDbLoggingFilter.java | 297 +++ .../sdnc/filters/RequestResponseLoggingFilter.java | 16 +- pom.xml | 11 +- rootpom/pom.xml | 135 +- sli/common/pom.xml | 2 +- .../org/openecomp/sdnc/sli/BreakNodeException.java | 46 + .../java/org/openecomp/sdnc/sli/MessageWriter.java | 302 +++ .../openecomp/sdnc/sli/SvcLogicExprListener.java | 60 +- .../sdnc/sli/SvcLogicExpressionFactory.java | 2 +- .../sdnc/sli/provider/BreakNodeExecutor.java | 42 + .../sdnc/sli/provider/CallNodeExecutor.java | 23 +- .../sdnc/sli/provider/DeleteNodeExecutor.java | 30 +- .../sdnc/sli/provider/ExecuteNodeExecutor.java | 93 +- .../sdnc/sli/provider/ExistsNodeExecutor.java | 27 +- .../sdnc/sli/provider/ForNodeExecutor.java | 24 +- .../sdnc/sli/provider/GetResourceNodeExecutor.java | 28 +- .../sdnc/sli/provider/IsAvailableNodeExecutor.java | 27 +- .../openecomp/sdnc/sli/provider/MdsalHelper.java | 2193 +++++++++----------- .../sdnc/sli/provider/NotifyNodeExecutor.java | 30 +- .../sdnc/sli/provider/RecordNodeExecutor.java | 38 +- .../sdnc/sli/provider/ReleaseNodeExecutor.java | 30 +- .../sdnc/sli/provider/ReserveNodeExecutor.java | 27 +- .../sdnc/sli/provider/SaveNodeExecutor.java | 27 +- .../sdnc/sli/provider/SetNodeExecutor.java | 2 +- .../sdnc/sli/provider/SvcLogicActivator.java | 1 + .../sli/provider/SvcLogicExpressionResolver.java | 8 +- .../sdnc/sli/provider/SvcLogicServiceImpl.java | 2 + .../sdnc/sli/provider/SwitchNodeExecutor.java | 3 +- .../sdnc/sli/provider/UpdateNodeExecutor.java | 26 +- .../org/openecomp/sdnc/sli/provider/BadPlugin.java | 56 + .../sdnc/sli/provider/ExecuteNodeExecutorTest.java | 57 + .../sdnc/sli/provider/LunchSelectorPlugin.java | 78 + .../sdnc/sli/provider/MdsalHelperTest.java | 43 + .../sdnc/sli/provider/MdsalHelperTesterUtil.java | 37 + .../openecomp/sdnc/sli/provider/PluginTest.java | 106 + .../sdnc/sli/provider/VoidDummyPlugin.java | 38 + sliPluginUtils/provider/pom.xml | 6 + .../openecomp/sdnc/sli/SliPluginUtils/DME2.java | 110 + .../sdnc/sli/SliPluginUtils/SliPluginUtils.java | 1232 ++++++----- .../SliPluginUtils/SliPluginUtilsActivator.java | 73 +- .../sdnc/sli/SliPluginUtils/SliStringUtils.java | 396 ++++ .../sli/SliPluginUtils/SvcLogicContextList.java | 29 +- .../sli/SliPluginUtils/CheckParametersTest.java | 116 ++ .../sdnc/sli/SliPluginUtils/Dme2Test.java | 109 + .../SliPluginUtils_StaticFunctionsTest.java | 250 +++ .../SliPluginUtils_ctxSortListTest.java | 97 + .../sli/SliPluginUtils/SliStringUtilsTest.java | 244 +++ sliapi/installer/pom.xml | 1 - sliapi/model/pom.xml | 4 +- sliapi/model/src/main/yang/sliapi.yang | 5 +- sliapi/provider/pom.xml | 2 +- .../org/openecomp/sdnc/sliapi/sliapiProvider.java | 37 +- 111 files changed, 19969 insertions(+), 2414 deletions(-) create mode 100644 .scanignore create mode 100755 dblib/common/LICENSE create mode 100755 dblib/common/NOTICE create mode 100755 dblib/common/doc/changelog.xml create mode 100755 dblib/common/doc/jdbc-pool.xml create mode 100755 dblib/common/doc/package.xsl create mode 100755 dblib/common/doc/project.xml create mode 100755 dblib/common/pom.xml create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ClassLoaderUtil.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/MultiLockFairBlockingQueue.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolExhaustedException.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolUtilities.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/TrapException.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/Validator.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/QueryTimeoutInterceptor.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ResetAbandonedTimer.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmxMBean.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementFinalizer.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/mbeans-descriptors.xml create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java create mode 100644 dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml create mode 100644 dblib/common/src/main/java/org/openecomp/sdnc/sli/resource/common/CommonActivator.java create mode 100644 dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jdbc/JdbcDBCachedDataSource.java delete mode 100644 dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jndi/JNDIDbResourceManagerFactory.java delete mode 100644 dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jndi/JndiCachedDataSource.java create mode 100644 filters/provider/src/main/java/org/openecomp/sdnc/filters/RequestResponseDbLoggingFilter.java create mode 100644 sli/common/src/main/java/org/openecomp/sdnc/sli/BreakNodeException.java create mode 100644 sli/common/src/main/java/org/openecomp/sdnc/sli/MessageWriter.java create mode 100644 sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/BreakNodeExecutor.java create mode 100644 sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/BadPlugin.java create mode 100644 sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/ExecuteNodeExecutorTest.java create mode 100644 sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/LunchSelectorPlugin.java create mode 100644 sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/MdsalHelperTest.java create mode 100644 sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/MdsalHelperTesterUtil.java create mode 100644 sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/PluginTest.java create mode 100644 sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/VoidDummyPlugin.java create mode 100644 sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/DME2.java create mode 100644 sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliStringUtils.java create mode 100644 sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/CheckParametersTest.java create mode 100644 sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/Dme2Test.java create mode 100644 sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils_StaticFunctionsTest.java create mode 100644 sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils_ctxSortListTest.java create mode 100644 sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliStringUtilsTest.java diff --git a/.gitignore b/.gitignore index 8f8f95a..f9801b8 100755 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ ExprGrammarLexer.tokens # BlackDuck generated file sdnc-core_bdio.jsonld +blackDuckHubProjectName.txt +blackDuckHubProjectVersionName.txt diff --git a/.scanignore b/.scanignore new file mode 100644 index 0000000..06ff17a --- /dev/null +++ b/.scanignore @@ -0,0 +1,5 @@ +.git +./dblib/provider/src/main/resources/dblib.properties:org.openecomp.sdnc.sli.jdbc.password=gamma +./sli/common/src/test/resources/svclogic.properties:org.openecomp.sdnc.sli.jdbc.password = gamma +./sli/provider/src/test/resources/svclogic.properties:org.openecomp.sdnc.sli.jdbc.password = gamma +./sli/recording/src/main/resources/svclogic.properties:org.openecomp.sdnc.sli.jdbc.password = gamma diff --git a/dblib/common/LICENSE b/dblib/common/LICENSE new file mode 100755 index 0000000..f49a4e1 --- /dev/null +++ b/dblib/common/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/dblib/common/NOTICE b/dblib/common/NOTICE new file mode 100755 index 0000000..0b6fc4b --- /dev/null +++ b/dblib/common/NOTICE @@ -0,0 +1,6 @@ +Apache Tomcat JDBC Pool +Copyright 2008-2016 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + diff --git a/dblib/common/doc/changelog.xml b/dblib/common/doc/changelog.xml new file mode 100755 index 0000000..2d7ddf8 --- /dev/null +++ b/dblib/common/doc/changelog.xml @@ -0,0 +1,133 @@ + + + +]> + + + + &project; + + + Filip Hanik + Changelog + + + + +
+

+ Starting with Apache Tomcat 7.0.19 in July 2011, Tomcat JDBC Connection Pool + is built and released as a component in official releases of Tomcat. + The changes are now listed in "jdbc-pool" sections of Apache Tomcat + changelog file. This changelog file is obsolete. +

+
+ +
+ + + 1207712 Pool cleaner should be a global thread, not spawn one thread per connection pool. (fhanik) + 1073531 50805 Only initialize connections once when async (fhanik) + 1076380 50857 Correctly handle timeouts when the pool is busy when async (fhanik) + Added QueryTimeoutInterceptor to be able to configure timeouts on running queries automatically. + + +
+ +
+ + + 1069864 50759 Correctly set validation timestamp when using external validator.(fhanik) + + +
+ +
+ + + 1060998 50613 Fix concurrency issue around pool size calculation.(fhanik) + + +
+
+ + + 1057743 Make sure passwords are masked.(fhanik) + + +
+
+ + + 997321 Ensure threads borrowing connections do not + get stuck waiting for a new connection if a connection is released in + another thread. (markt) + 995432 Make interceptor class names, property names + and property values tolerant of whitespace by trimming the values before + use. (markt) + 995091 49831 Make sure pooled XAConnections are + closed when the connection pool shuts down. Patch provided by Daniel + Mikusa. (markt) + 995087 Code clean-up. Remove some unused code. (markt) + + 995083 Update to Tomcat 6.0.29 (for JULI). (markt) + + 992409 Code clean-up. Reduce sequences of three or more + blank lines to two blank lines. (markt) + 952811, 995095 48814 Add Validator + interface and allow users to configure a Validator class name. Patch + provided by Matt Passell. (markt) + 948073 Code clean-up. Remove unused imports. (markt) + + 943434 49224 Only try setting the username and + password if they are non-null. Patch provided by Matt Passell. (markt) + + 943032 49269 Set maxIdle to maxActive by + default to prevent warning on start when maxIdle > maxActive. Patch + provided by Matt Passell. (markt) + 940574 49241 Don't ignore the + suspectTimeout property. (fhanik) + 939320 Fix svn:keywords for property replacement. + (kkolinko) + 931550, 934651, 934677 Add a + statement cache. (fhanik) + 919076 Improve XA support. (fhanik) + 915940 48392 Add an interceptor to wrap + Statements and ResultSets to prevent access to the physical connection. + (fhanik) + 912026 Call setTransactionIsolation() before + anything else as some drivers require this to be the first call. (fhanik) + + 900017 Update Javadoc for XADataSource. (kkolinko) + + + +
+
+ + + 720253 Document how to use interceptors + 717972 Added an interceptor that will clean up non closed statements when a connection is returned to the pool. (org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer) + 713763 Improve connection state handling + 713763 Improve connection state handling + + +
+ +
diff --git a/dblib/common/doc/jdbc-pool.xml b/dblib/common/doc/jdbc-pool.xml new file mode 100755 index 0000000..8d69624 --- /dev/null +++ b/dblib/common/doc/jdbc-pool.xml @@ -0,0 +1,988 @@ + + + +]> + + + &project; + + + Filip Hanik + The Tomcat JDBC Connection Pool + + + + +
+ +
+ +
+ +

The JDBC Connection Pool org.apache.tomcat.jdbc.pool + is a replacement or an alternative to the Apache Commons DBCP + connection pool.

+ +

So why do we need a new connection pool?

+ +

Here are a few of the reasons:

+
    +
  1. Commons DBCP 1.x is single threaded. In order to be thread safe + Commons locks the entire pool for short periods during both object + allocation and object return. Note that this does not apply to + Commons DBCP 2.x.
  2. +
  3. Commons DBCP 1.x can be slow. As the number of logical CPUs grows and + the number of concurrent threads attempting to borrow or return + objects increases, the performance suffers. For highly concurrent + systems the impact can be significant. Note that this does not apply + to Commons DBCP 2.x.
  4. +
  5. Commons DBCP is over 60 classes. tomcat-jdbc-pool core is 8 classes, + hence modifications for future requirement will require much less + changes. This is all you need to run the connection pool itself, the + rest is gravy.
  6. +
  7. Commons DBCP uses static interfaces. This means you have to use the + right version for a given JRE version or you may see + NoSuchMethodException exceptions.
  8. +
  9. It's not worth rewriting over 60 classes, when a connection pool can + be accomplished with a much simpler implementation.
  10. +
  11. Tomcat jdbc pool implements the ability retrieve a connection + asynchronously, without adding additional threads to the library + itself.
  12. +
  13. Tomcat jdbc pool is a Tomcat module, it depends on Tomcat JULI, a + simplified logging framework used in Tomcat.
  14. +
  15. Retrieve the underlying connection using the + javax.sql.PooledConnection interface.
  16. +
  17. Starvation proof. If a pool is empty, and threads are waiting for a + connection, when a connection is returned, the pool will awake the + correct thread waiting. Most pools will simply starve.
  18. +
+ +

Features added over other connection pool implementations

+
    +
  1. Support for highly concurrent environments and multi core/cpu systems.
  2. +
  3. Dynamic implementation of interface, will support java.sql and javax.sql interfaces for + your runtime environment (as long as your JDBC driver does the same), even when compiled with a lower version of the JDK.
  4. +
  5. Validation intervals - we don't have to validate every single time we use the connection, we can do this + when we borrow or return the connection, just not more frequent than an interval we can configure.
  6. +
  7. Run-Once query, a configurable query that will be run only once, when the connection to the database is established. + Very useful to setup session settings, that you want to exist during the entire time the connection is established.
  8. +
  9. Ability to configure custom interceptors. + This allows you to write custom interceptors to enhance the functionality. You can use interceptors to gather query stats, + cache session states, reconnect the connection upon failures, retry queries, cache query results, and so on. + Your options are endless and the interceptors are dynamic, not tied to a JDK version of a + java.sql/javax.sql interface.
  10. +
  11. High performance - we will show some differences in performance later on
  12. +
  13. Extremely simple, due to the very simplified implementation, the line count and source file count are very low, compare with c3p0 + that has over 200 source files(last time we checked), Tomcat jdbc has a core of 8 files, the connection pool itself is about half + that. As bugs may occur, they will be faster to track down, and easier to fix. Complexity reduction has been a focus from inception.
  14. +
  15. Asynchronous connection retrieval - you can queue your request for a connection and receive a Future<Connection> back.
  16. +
  17. Better idle connection handling. Instead of closing connections directly, it can still pool connections and sizes the idle pool with a smarter algorithm.
  18. +
  19. You can decide at what moment connections are considered abandoned, is it when the pool is full, or directly at a timeout + by specifying a pool usage threshold. +
  20. +
  21. The abandon connection timer will reset upon a statement/query activity. Allowing a connections that is in use for a long time to not timeout. + This is achieved using the ResetAbandonedTimer +
  22. +
  23. Close connections after they have been connected for a certain time. Age based close upon return to the pool. +
  24. +
  25. Get JMX notifications and log entries when connections are suspected for being abandoned. This is similar to + the removeAbandonedTimeout but it doesn't take any action, only reports the information. + This is achieved using the suspectTimeout attribute.
  26. +
  27. Connections can be retrieved from a java.sql.Driver, javax.sql.DataSource or javax.sql.XADataSource + This is achieved using the dataSource and dataSourceJNDI attributes.
  28. +
  29. XA connection support
  30. +
+ + +
+
+

+ Usage of the Tomcat connection pool has been made to be as simple as possible, for those of you that are familiar with commons-dbcp, the + transition will be very simple. Moving from other connection pools is also fairly straight forward. +

+ +

The Tomcat connection pool offers a few additional features over what most other pools let you do:

+
    +
  • initSQL - the ability to run a SQL statement exactly once, when the connection is created
  • +
  • validationInterval - in addition to running validations on connections, avoid running them too frequently.
  • +
  • jdbcInterceptors - flexible and pluggable interceptors to create any customizations around the pool, + the query execution and the result set handling. More on this in the advanced section.
  • +
  • fairQueue - Set the fair flag to true to achieve thread fairness or to use asynchronous connection retrieval
  • +
+
+ +

+ The Tomcat Connection pool is configured as a resource described in The Tomcat JDBC documentation + With the only difference being that you have to specify the factory attribute and set the value to + org.apache.tomcat.jdbc.pool.DataSourceFactory +

+
+ +

+ The connection pool only has another dependency, and that is on tomcat-juli.jar. + To configure the pool in a stand alone project using bean instantiation, the bean to instantiate is + org.apache.tomcat.jdbc.pool.DataSource. The same attributes (documented below) as you use to configure a connection + pool as a JNDI resource, are used to configure a data source as a bean. +

+
+ +

+ The connection pool object exposes an MBean that can be registered. + In order for the connection pool object to create the MBean, the flag jmxEnabled has to be set to true. + This doesn't imply that the pool will be registered with an MBean server, merely that the MBean is created. + In a container like Tomcat, Tomcat itself registers the DataSource with the MBean server, the + org.apache.tomcat.jdbc.pool.DataSource object will then register the actual + connection pool MBean. + If you're running outside of a container, you can register the DataSource yourself under any object name you specify, + and it propagates the registration to the underlying pool. To do this you would call mBeanServer.registerMBean(dataSource.getPool().getJmxPool(),objectname). + Prior to this call, ensure that the pool has been created by calling dataSource.createPool(). +

+
+ +
+
+

To provide a very simple switch to and from commons-dbcp and tomcat-jdbc-pool, + Most attributes are the same and have the same meaning.

+ + + +

factory is required, and the value should be org.apache.tomcat.jdbc.pool.DataSourceFactory

+
+ +

Type should always be javax.sql.DataSource or javax.sql.XADataSource

+

Depending on the type a org.apache.tomcat.jdbc.pool.DataSource or a org.apache.tomcat.jdbc.pool.XADataSource will be created.

+
+
+
+ + +

System properties are JVM wide, affect all pools created in the JVM

+ + +

(boolean) Controls classloading of dynamic classes, such as + JDBC drivers, interceptors and validators. If set to + false, default value, the pool will first attempt + to load using the current loader (i.e. the class loader that + loaded the pool classes) and if class loading fails attempt to + load using the thread context loader. Set this value to + true, if you wish to remain backwards compatible + with Apache Tomcat 8.0.8 and earlier, and only attempt the + current loader. + If not set then the default value is false. +

+
+
+
+ + +

These attributes are shared between commons-dbcp and tomcat-jdbc-pool, in some cases default values are different.

+ + + +

(boolean) The default auto-commit state of connections created by this pool. If not set, default is JDBC driver default (If not set then the setAutoCommit method will not be called.)

+
+ + +

(boolean) The default read-only state of connections created by this pool. If not set then the setReadOnly method will not be called. (Some drivers don't support read only mode, ex: Informix)

+
+ + +

(String) The default TransactionIsolation state of connections created by this pool. One of the following: (see javadoc )

+
    +
  • NONE
  • +
  • READ_COMMITTED
  • +
  • READ_UNCOMMITTED
  • +
  • REPEATABLE_READ
  • +
  • SERIALIZABLE
  • +
+

If not set, the method will not be called and it defaults to the JDBC driver.

+
+ + +

(String) The default catalog of connections created by this pool.

+
+ + +

(String) The fully qualified Java class name of the JDBC driver to be used. The driver has to be accessible + from the same classloader as tomcat-jdbc.jar +

+
+ + +

(String) The connection username to be passed to our JDBC driver to establish a connection. + Note that method DataSource.getConnection(username,password) + by default will not use credentials passed into the method, + but will use the ones configured here. See alternateUsernameAllowed + property for more details. +

+
+ + +

(String) The connection password to be passed to our JDBC driver to establish a connection. + Note that method DataSource.getConnection(username,password) + by default will not use credentials passed into the method, + but will use the ones configured here. See alternateUsernameAllowed + property for more details. +

+
+ + +

(int) The maximum number of active connections that can be allocated from this pool at the same time. + The default value is 100

+
+ + +

(int) The maximum number of connections that should be kept in the pool at all times. + Default value is maxActive:100 + Idle connections are checked periodically (if enabled) and + connections that been idle for longer than minEvictableIdleTimeMillis + will be released. (also see testWhileIdle)

+
+ + +

+ (int) The minimum number of established connections that should be kept in the pool at all times. + The connection pool can shrink below this number if validation queries fail. + Default value is derived from initialSize:10 (also see testWhileIdle) +

+
+ + +

(int)The initial number of connections that are created when the pool is started. + Default value is 10

+
+ + +

(int) The maximum number of milliseconds that the pool will wait (when there are no available connections) + for a connection to be returned before throwing an exception. + Default value is 30000 (30 seconds)

+
+ + +

(boolean) The indication of whether objects will be validated before being borrowed from the pool. + If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another. + NOTE - for a true value to have any effect, the validationQuery + or validatorClassName parameter must be set to a non-null string. + In order to have a more efficient validation, see validationInterval. + Default value is false +

+
+ + +

(boolean) The indication of whether objects will be validated when a connection is first created. + If an object fails to validate, it will be throw SQLException. + NOTE - for a true value to have any effect, the validationQuery, initSQL + or validatorClassName parameter must be set to a non-null string. + Default value is false +

+
+ + +

(boolean) The indication of whether objects will be validated before being returned to the pool. + NOTE - for a true value to have any effect, the validationQuery + or validatorClassName parameter must be set to a non-null string. + The default value is false. +

+
+ + +

(boolean) The indication of whether objects will be validated by the idle object evictor (if any). + If an object fails to validate, it will be dropped from the pool. + NOTE - for a true value to have any effect, the validationQuery + or validatorClassName parameter must be set to a non-null string. + The default value is false and this property has to be set in order for the + pool cleaner/test thread is to run (also see timeBetweenEvictionRunsMillis) +

+
+ + +

(String) The SQL query that will be used to validate connections from this pool before returning them to the caller. + If specified, this query does not have to return any data, it just can't throw a SQLException. + The default value is null. + Example values are SELECT 1(mysql), select 1 from dual(oracle), SELECT 1(MS Sql Server) +

+
+ + +

(int) The timeout in seconds before a connection validation queries fail. This works by calling + java.sql.Statement.setQueryTimeout(seconds) on the statement that executes the validationQuery. + The pool itself doesn't timeout the query, it is still up to the JDBC driver to enforce query timeouts. + A value less than or equal to zero will disable this feature. + The default value is -1. +

+
+ + +

(String) The name of a class which implements the + org.apache.tomcat.jdbc.pool.Validator interface and + provides a no-arg constructor (may be implicit). If specified, the + class will be used to create a Validator instance which is then used + instead of any validation query to validate connections. The default + value is null. An example value is + com.mycompany.project.SimpleValidator. +

+
+ + +

(int) The number of milliseconds to sleep between runs of the idle connection validation/cleaner thread. + This value should not be set under 1 second. It dictates how often we check for idle, abandoned connections, and how often + we validate idle connections. + The default value is 5000 (5 seconds).
+

+
+ + +

(int) Property not used in tomcat-jdbc-pool.

+
+ + +

(int) The minimum amount of time an object may sit idle in the pool before it is eligible for eviction. + The default value is 60000 (60 seconds).

+
+ + +

(boolean) Property not used. Access can be achieved by calling unwrap on the pooled connection. + see javax.sql.DataSource interface, or call getConnection through reflection or + cast the object as javax.sql.PooledConnection

+
+ + +

(boolean) Flag to remove abandoned connections if they exceed the removeAbandonedTimeout. + If set to true a connection is considered abandoned and eligible for removal if it has been in use + longer than the removeAbandonedTimeout Setting this to true can recover db connections from + applications that fail to close a connection. See also logAbandoned + The default value is false.

+
+ + +

(int) Timeout in seconds before an abandoned(in use) connection can be removed. + The default value is 60 (60 seconds). The value should be set to the longest running query your applications + might have.

+
+ + +

(boolean) Flag to log stack traces for application code which abandoned a Connection. + Logging of abandoned Connections adds overhead for every Connection borrow because a stack trace has to be generated. + The default value is false.

+
+ + +

(String) The connection properties that will be sent to our JDBC driver when establishing new connections. + Format of the string must be [propertyName=property;]* + NOTE - The "user" and "password" properties will be passed explicitly, so they do not need to be included here. + The default value is null.

+
+ + +

(boolean) Property not used.

+
+ + +

(int) Property not used.

+
+ +
+ +
+ + + + + + +

(String) A custom query to be run when a connection is first created. + The default value is null.

+
+ + +

(String) A semicolon separated list of classnames extending + org.apache.tomcat.jdbc.pool.JdbcInterceptor class. + See Configuring JDBC interceptors + below for more detailed description of syntaz and examples. +

+

+ These interceptors will be inserted as an interceptor into the chain + of operations on a java.sql.Connection object. + The default value is null. +

+

+ Predefined interceptors:
+ org.apache.tomcat.jdbc.pool.interceptor.
ConnectionState
+ - keeps track of auto commit, read only, catalog and transaction isolation level.
+ org.apache.tomcat.jdbc.pool.interceptor.
StatementFinalizer
+ - keeps track of opened statements, and closes them when the connection is returned to the pool. +

+

+ More predefined interceptors are described in detail in the + JDBC Interceptors section. +

+
+ + +

(long) avoid excess validation, only run validation at most at this frequency - time in milliseconds. + If a connection is due for validation, but has been validated previously within this interval, it will not be validated again. + The default value is 3000 (3 seconds).

+
+ + +

(boolean) Register the pool with JMX or not. + The default value is true.

+
+ + +

(boolean) Set to true if you wish that calls to getConnection should be treated + fairly in a true FIFO fashion. This uses the org.apache.tomcat.jdbc.pool.FairBlockingQueue + implementation for the list of the idle connections. The default value is true. + This flag is required when you want to use asynchronous connection retrieval.
+ Setting this flag ensures that threads receive connections in the order they arrive.
+ During performance tests, there is a very large difference in how locks + and lock waiting is implemented. When fairQueue=true + there is a decision making process based on what operating system the system is running. + If the system is running on Linux (property os.name=Linux. + To disable this Linux specific behavior and still use the fair queue, simply add the property + org.apache.tomcat.jdbc.pool.FairBlockingQueue.ignoreOS=true to your system properties + before the connection pool classes are loaded. +

+
+ + +

(int) Connections that have been abandoned (timed out) wont get closed and reported up unless + the number of connections in use are above the percentage defined by abandonWhenPercentageFull. + The value should be between 0-100. + The default value is 0, which implies that connections are eligible for closure as soon + as removeAbandonedTimeout has been reached.

+
+ + +

(long) Time in milliseconds to keep this connection. This attribute + works both when returning connection and when borrowing connection. + When a connection is borrowed from the pool, the pool will check to see + if the now - time-when-connected > maxAge has been reached + , and if so, it reconnects before borrow it. When a connection is + returned to the pool, the pool will check to see if the + now - time-when-connected > maxAge has been reached, and + if so, it closes the connection rather than returning it to the pool. + The default value is 0, which implies that connections + will be left open and no age check will be done upon borrowing from the + pool and returning the connection to the pool.

+
+ + +

(boolean) Set to true if you wish the ProxyConnection class to use String.equals and set to false + when you wish to use == when comparing method names. This property does not apply to added interceptors as those are configured individually. + The default value is true. +

+
+ +

(int) Timeout value in seconds. Default value is 0.
+ Similar to to the removeAbandonedTimeout value but instead of treating the connection + as abandoned, and potentially closing the connection, this simply logs the warning if + logAbandoned is set to true. If this value is equal or less than 0, no suspect + checking will be performed. Suspect checking only takes place if the timeout value is larger than 0 and + the connection was not abandoned or if abandon check is disabled. If a connection is suspect a WARN message gets + logged and a JMX notification gets sent once. +

+
+ +

(boolean) If autoCommit==false then the pool can terminate the transaction by calling rollback on the connection as it is returned to the pool + Default value is false.
+

+
+ +

(boolean) If autoCommit==false then the pool can complete the transaction by calling commit on the connection as it is returned to the pool + If rollbackOnReturn==true then this attribute is ignored. + Default value is false.
+

+
+ +

(boolean) By default, the jdbc-pool will ignore the + DataSource.getConnection(username,password) + call, and simply return a previously pooled connection under the globally configured properties username and password, for performance reasons. +

+

+ The pool can however be configured to allow use of different credentials + each time a connection is requested. To enable the functionality described in the + DataSource.getConnection(username,password) + call, simply set the property alternateUsernameAllowed + to true.
+ Should you request a connection with the credentials user1/password1 and the connection + was previously connected using different user2/password2, the connection will be closed, + and reopened with the requested credentials. This way, the pool size is still managed + on a global level, and not on a per schema level.
+ The default value is false.
+ This property was added as an enhancement to bug 50025. +

+
+ +

(javax.sql.DataSource) Inject a data source to the connection pool, and the pool will use the data source to retrieve connections instead of establishing them using the java.sql.Driver interface. + This is useful when you wish to pool XA connections or connections established using a data source instead of a connection string. Default value is null +

+
+ +

(String) The JNDI name for a data source to be looked up in JNDI and then used to establish connections to the database. See the dataSource attribute. Default value is null +

+
+ +

(boolean) Set this to true if you wish to put a facade on your connection so that it cannot be reused after it has been closed. This prevents a thread holding on to a + reference of a connection it has already called closed on, to execute queries on it. Default value is true. +

+
+ +

(boolean) Set this to true to log errors during the validation phase to the log file. If set to true, errors will be logged as SEVERE. Default value is false for backwards compatibility. +

+
+ +

(boolean) Set this to true to propagate the interrupt state for a thread that has been interrupted (not clearing the interrupt state). Default value is false for backwards compatibility. +

+
+ +

(boolean) Flag whether ignore error of connection creation while initializing the pool. + Set to true if you want to ignore error of connection creation while initializing the pool. + Set to false if you want to fail the initialization of the pool by throwing exception. + The default value is false. +

+
+ +
+
+
+
+ +

To see an example of how to use an interceptor, take a look at + org.apache.tomcat.jdbc.pool.interceptor.ConnectionState. + This simple interceptor is a cache of three attributes, transaction isolation level, auto commit and read only state, + in order for the system to avoid not needed roundtrips to the database. +

+

Further interceptors will be added to the core of the pool as the need arises. Contributions are always welcome!

+

Interceptors are of course not limited to just java.sql.Connection but can be used to wrap any + of the results from a method invokation as well. You could build query performance analyzer that provides JMX notifications when a + query is running longer than the expected time.

+
+ +

Configuring JDBC interceptors is done using the jdbcInterceptors property. + The property contains a list of semicolon separated class names. If the + classname is not fully qualified it will be prefixed with the + org.apache.tomcat.jdbc.pool.interceptor. prefix. +

+

Example:
+ + jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; + org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer" + +
+ is the same as +
+ jdbcInterceptors="ConnectionState;StatementFinalizer" +

+

+ Interceptors can have properties as well. Properties for an interceptor + are specified within parentheses after the class name. Several properties + are separated by commas. +

+

Example:
+ + jdbcInterceptors="ConnectionState;StatementFinalizer(useEquals=true)" + +

+

+ Extra whitespace characters around class names, property names and values + are ignored. +

+
+ +

Abstract base class for all interceptors, cannot be instantiated.

+ + +

(boolean) Set to true if you wish the ProxyConnection class to use String.equals and set to false + when you wish to use == when comparing method names. + The default value is true. +

+
+
+
+ +

Caches the connection for the following attributes autoCommit, readOnly, + transactionIsolation and catalog. + It is a performance enhancement to avoid roundtrip to the database when getters are called or setters are called with an already set value. +

+ + +
+ +

Keeps track of all statements created using createStatement, prepareStatement or prepareCall + and closes these statements when the connection is returned to the pool. +

+ + +

(boolean as String) Enable tracing of unclosed statements. + When enabled and a connection is closed, and statements are not closed, + the interceptor will log all stack traces. + The default value is false. +

+
+
+
+ +

Caches PreparedStatement and/or CallableStatement + instances on a connection. +

+

The statements are cached per connection. + The count limit is counted globally for all connections that belong to + the same pool. Once the count reaches max, subsequent + statements are not returned to the cache and are closed immediately. +

+ + +

(boolean as String) Enable caching of PreparedStatement + instances created using prepareStatement calls. + The default value is true. +

+
+ +

(boolean as String) Enable caching of CallableStatement + instances created using prepareCall calls. + The default value is false. +

+
+ +

(int as String) Limit on the count of cached statements across + the connection pool. + The default value is 50. +

+
+
+
+ +

See 48392. Interceptor to wrap statements and result sets in order to prevent access to the actual connection + using the methods ResultSet.getStatement().getConnection() and Statement.getConnection() +

+ + +
+ +

Automatically calls java.sql.Statement.setQueryTimeout(seconds) when a new statement is created. + The pool itself doesn't timeout the query, it is still up to the JDBC driver to enforce query timeouts. +

+ + +

(int as String) The number of seconds to set for the query timeout. + A value less than or equal to zero will disable this feature. + The default value is 1 seconds. +

+
+
+
+ +

Keeps track of query performance and issues log entries when queries exceed a time threshold of fail. + The log level used is WARN +

+ + +

(int as String) The number of milliseconds a query has to exceed before issuing a log alert. + The default value is 1000 milliseconds. +

+
+ +

(int as String) The maximum number of queries to keep track of in order to preserve memory space. + A value less than or equal to 0 will disable this feature. + The default value is 1000. +

+
+ +

(boolean as String) Set to true if you wish to log slow queries. + The default value is true. +

+
+ +

(boolean as String) Set to true if you wish to log failed queries. + The default value is false. +

+
+
+
+ +

Extends the SlowQueryReport and in addition to log entries it issues JMX notification + for monitoring tools to react to. Inherits all the attributes from its parent class. + This class uses Tomcat's JMX engine so it wont work outside of the Tomcat container. + By default, JMX notifications are sent through the ConnectionPool mbean if it is enabled. + The SlowQueryReportJmx can also register an MBean if notifyPool=false +

+ + +

(boolean as String) Set to false if you want JMX notifications to go to the SlowQueryReportJmx MBean + The default value is true. +

+
+ +

(String) Define a valid javax.management.ObjectName string that will be used to register this object with the platform mbean server + The default value is null and the object will be registered using + tomcat.jdbc:type=org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx,name=the-name-of-the-pool +

+
+
+
+ +

+ The abandoned timer starts when a connection is checked out from the pool. + This means if you have a 30second timeout and run 10x10second queries using the connection + it will be marked abandoned and potentially reclaimed depending on the abandonWhenPercentageFull + attribute. + Using this interceptor it will reset the checkout timer every time you perform an operation on the connection or execute a + query successfully. +

+ + +
+
+ +
+

Other examples of Tomcat configuration for JDBC usage can be found in the Tomcat documentation.

+ +

Here is a simple example of how to create and use a data source.

+ +
+ +

And here is an example on how to configure a resource for JNDI lookups

+]]> + +
+ +

The Tomcat JDBC connection pool supports asynchronous connection retrieval without adding additional threads to the + pool library. It does this by adding a method to the data source called Future<Connection> getConnectionAsync(). + In order to use the async retrieval, two conditions must be met: +

+
    +
  1. You must configure the fairQueue property to be true.
  2. +
  3. You will have to cast the data source to org.apache.tomcat.jdbc.pool.DataSource
  4. +
+ An example of using the async feature is show below. + future = datasource.getConnectionAsync(); + while (!future.isDone()) { + System.out.println("Connection is not yet available. Do some background work"); + try { + Thread.sleep(100); //simulate work + }catch (InterruptedException x) { + Thread.currentThread().interrupt(); + } + } + con = future.get(); //should return instantly + Statement st = con.createStatement(); + ResultSet rs = st.executeQuery("select * from user");]]> + +
+ +

Interceptors are a powerful way to enable, disable or modify functionality on a specific connection or its sub components. + There are many different use cases for when interceptors are useful. By default, and for performance reasons, the connection pool is stateless. + The only state the pool itself inserts are defaultAutoCommit, defaultReadOnly, defaultTransactionIsolation, defaultCatalog if + these are set. These 4 properties are only set upon connection creation. Should these properties be modified during the usage of the connection, + the pool itself will not reset them.

+

An interceptor has to extend the org.apache.tomcat.jdbc.pool.JdbcInterceptor class. This class is fairly simple, + You will need to have a no arg constructor

+ +

+ When a connection is borrowed from the pool, the interceptor can initialize or in some other way react to the event by implementing the +

+ +

+ method. This method gets called with two parameters, a reference to the connection pool itself ConnectionPool parent + and a reference to the underlying connection PooledConnection con. +

+

+ When a method on the java.sql.Connection object is invoked, it will cause the +

+ +

+ method to get invoked. The Method method is the actual method invoked, and Object[] args are the arguments. + To look at a very simple example, where we demonstrate how to make the invokation to java.sql.Connection.close() a noop + if the connection has been closed +

+ +

+ There is an observation being made. It is the comparison of the method name. One way to do this would be to do + "close".equals(method.getName()). + Above we see a direct reference comparison between the method name and static final String reference. + According to the JVM spec, method names and static final String end up in a shared constant pool, so the reference comparison should work. + One could of course do this as well: +

+ +

+ The compare(String,Method) will use the useEquals flag on an interceptor and do either reference comparison or + a string value comparison when the useEquals=true flag is set. +

+

Pool start/stop
+ When the connection pool is started or closed, you can be notifed. You will only be notified once per interceptor class + even though it is an instance method. and you will be notified using an interceptor currently not attached to a pool. +

+ +

+ When overriding these methods, don't forget to call super if you are extending a class other than JdbcInterceptor +

+

Configuring interceptors
+ Interceptors are configured using the jdbcInterceptors property or the setJdbcInterceptors method. + An interceptor can have properties, and would be configured like this +

+ + +

Interceptor properties
+ Since interceptors can have properties, you need to be able to read the values of these properties within your + interceptor. Taking an example like the one above, you can override the setProperties method. +

+ properties) { + super.setProperties(properties); + final String myprop = "myprop"; + InterceptorProperty p1 = properties.get(myprop); + if (p1!=null) { + setMyprop(Long.parseLong(p1.getValue())); + } + }]]> + +
+ +

Connection pools create wrappers around the actual connection in order to properly pool them. + We also create interceptors in these wrappers to be able to perform certain functions. + If there is a need to retrieve the actual connection, one can do so using the javax.sql.PooledConnection + interface. +

+ + +
+ +
+ +
+

We build the JDBC pool code with 1.6, but it is backwards compatible down to 1.5 for runtime environment. For unit test, we use 1.6 and higher

+

Other examples of Tomcat configuration for JDBC usage can be found in the Tomcat documentation.

+ +

Building is pretty simple. The pool has a dependency on tomcat-juli.jar and in case you want the SlowQueryReportJmx

+ +

+ A build file can be found in the Tomcat source repository. +

+

+ As a convenience, a build file is also included where a simple build command will generate all files needed. +

+ ant download (downloads dependencies) + ant build (compiles and generates .jar files) + ant dist (creates a release package) + ant test (runs tests, expects a test database to be setup) + +

+ The system is structured for a Maven build, but does generate release artifacts. Just the library itself. +

+
+
+ + +
diff --git a/dblib/common/doc/package.xsl b/dblib/common/doc/package.xsl new file mode 100755 index 0000000..cdadd53 --- /dev/null +++ b/dblib/common/doc/package.xsl @@ -0,0 +1,249 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <xsl:value-of select="project/title"/> - <xsl:value-of select="properties/title"/> + + + + +

.

+ + + + +
+ + + + + + + + + + + + +
+ + + +
+ +
+
+ + + + + + + + + + + + +
+ + + +
+ +
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+            
+          
+ +
+ + + + + +
+
+
+ + + + + + + + + + + + + + + +
+ Attribute + + Description +
+ + + + + + + + +
+
+ + + + + + + + + + + + + + +
+ Property + + Description +
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/dblib/common/doc/project.xml b/dblib/common/doc/project.xml new file mode 100755 index 0000000..912903d --- /dev/null +++ b/dblib/common/doc/project.xml @@ -0,0 +1,31 @@ + + + + + Apache Tomcat JDBC Pool + + + The Apache Tomcat Servlet/JSP Container + + + + + + + diff --git a/dblib/common/pom.xml b/dblib/common/pom.xml new file mode 100755 index 0000000..fa25c98 --- /dev/null +++ b/dblib/common/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + org.openecomp.sdnc.core + dblib + 1.1.0-SNAPSHOT + + dblib-common + bundle + DBLIB Adaptor - Common + + UTF-8 + + + + + org.apache.tomcat + juli + 6.0.32 + + + junit + junit + 4.11 + test + + + org.apache.tomcat + tomcat-dbcp + 8.0.14 + test + + + com.h2database + h2 + 1.3.152 + test + + + equinoxSDK381 + org.eclipse.osgi + ${equinox.osgi.version} + + + mysql + mysql-connector-java + ${mysql.connector.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + org.apache.felix + maven-bundle-plugin + true + + + org.openecomp.sdnc.sli.resource.common.CommonActivator + + org.openecomp.sdnc.sli.resource.common;version=${project.version}, + org.apache.tomcat.jdbc;version=${project.version}, + org.apache.tomcat.jdbc.pool;version=${project.version}, + org.apache.tomcat.jdbc.naming;version=${project.version}, + org.apache.tomcat.jdbc.pool.interceptor;version=${project.version}, + org.apache.tomcat.jdbc.pool.jmx;version=${project.version} + * + + true + + ${project.basedir}/src/main/resources/META-INF + + + + + diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java new file mode 100644 index 0000000..c6112a1 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java @@ -0,0 +1,254 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.naming; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.spi.ObjectFactory; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jdbc.pool.ClassLoaderUtil; + +/** + * Simple way of configuring generic resources by using reflection. + * Example usage: + *

+ * <Resource factory="org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory"
+ *              name="jdbc/test"
+ *              type="org.apache.derby.jdbc.ClientXADataSource"
+ *              databaseName="sample"
+ *              createDatabase="create"
+ *              serverName="localhost"
+ *              port="1527"/>
+ * 
+ * + */ +public class GenericNamingResourcesFactory implements ObjectFactory { + private static final Log log = LogFactory.getLog(GenericNamingResourcesFactory.class); + + @Override + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception { + if ((obj == null) || !(obj instanceof Reference)) { + return null; + } + Reference ref = (Reference) obj; + Enumeration refs = ref.getAll(); + + String type = ref.getClassName(); + Object o = + ClassLoaderUtil.loadClass( + type, + GenericNamingResourcesFactory.class.getClassLoader(), + Thread.currentThread().getContextClassLoader()) + .newInstance(); + + while (refs.hasMoreElements()) { + RefAddr addr = refs.nextElement(); + String param = addr.getType(); + String value = null; + if (addr.getContent()!=null) { + value = addr.getContent().toString(); + } + if (setProperty(o, param, value)) { + + } else { + log.debug("Property not configured["+param+"]. No setter found on["+o+"]."); + } + } + return o; + } + + @SuppressWarnings("null") // setPropertyMethodVoid can't be null when used + private static boolean setProperty(Object o, String name, String value) { + if (log.isDebugEnabled()) + log.debug("IntrospectionUtils: setProperty(" + + o.getClass() + " " + name + "=" + value + ")"); + + String setter = "set" + capitalize(name); + + try { + Method methods[] = o.getClass().getMethods(); + Method setPropertyMethodVoid = null; + Method setPropertyMethodBool = null; + + // First, the ideal case - a setFoo( String ) method + for (int i = 0; i < methods.length; i++) { + Class paramT[] = methods[i].getParameterTypes(); + if (setter.equals(methods[i].getName()) && paramT.length == 1 + && "java.lang.String".equals(paramT[0].getName())) { + + methods[i].invoke(o, new Object[] { value }); + return true; + } + } + + // Try a setFoo ( int ) or ( boolean ) + for (int i = 0; i < methods.length; i++) { + boolean ok = true; + if (setter.equals(methods[i].getName()) + && methods[i].getParameterTypes().length == 1) { + + // match - find the type and invoke it + Class paramType = methods[i].getParameterTypes()[0]; + Object params[] = new Object[1]; + + // Try a setFoo ( int ) + if ("java.lang.Integer".equals(paramType.getName()) + || "int".equals(paramType.getName())) { + try { + params[0] = new Integer(value); + } catch (NumberFormatException ex) { + ok = false; + } + // Try a setFoo ( long ) + }else if ("java.lang.Long".equals(paramType.getName()) + || "long".equals(paramType.getName())) { + try { + params[0] = new Long(value); + } catch (NumberFormatException ex) { + ok = false; + } + + // Try a setFoo ( boolean ) + } else if ("java.lang.Boolean".equals(paramType.getName()) + || "boolean".equals(paramType.getName())) { + params[0] = Boolean.valueOf(value); + + // Try a setFoo ( InetAddress ) + } else if ("java.net.InetAddress".equals(paramType + .getName())) { + try { + params[0] = InetAddress.getByName(value); + } catch (UnknownHostException exc) { + if (log.isDebugEnabled()) + log.debug("IntrospectionUtils: Unable to resolve host name:" + value); + ok = false; + } + + // Unknown type + } else { + if (log.isDebugEnabled()) + log.debug("IntrospectionUtils: Unknown type " + + paramType.getName()); + } + + if (ok) { + methods[i].invoke(o, params); + return true; + } + } + + // save "setProperty" for later + if ("setProperty".equals(methods[i].getName())) { + if (methods[i].getReturnType()==Boolean.TYPE){ + setPropertyMethodBool = methods[i]; + }else { + setPropertyMethodVoid = methods[i]; + } + + } + } + + // Ok, no setXXX found, try a setProperty("name", "value") + if (setPropertyMethodBool != null || setPropertyMethodVoid != null) { + Object params[] = new Object[2]; + params[0] = name; + params[1] = value; + if (setPropertyMethodBool != null) { + try { + return ((Boolean) setPropertyMethodBool.invoke(o, params)).booleanValue(); + }catch (IllegalArgumentException biae) { + //the boolean method had the wrong + //parameter types. lets try the other + if (setPropertyMethodVoid!=null) { + setPropertyMethodVoid.invoke(o, params); + return true; + }else { + throw biae; + } + } + } else { + setPropertyMethodVoid.invoke(o, params); + return true; + } + } + + } catch (IllegalArgumentException ex2) { + log.warn("IAE " + o + " " + name + " " + value, ex2); + } catch (SecurityException ex1) { + if (log.isDebugEnabled()) + log.debug("IntrospectionUtils: SecurityException for " + + o.getClass() + " " + name + "=" + value + ")", ex1); + } catch (IllegalAccessException iae) { + if (log.isDebugEnabled()) + log.debug("IntrospectionUtils: IllegalAccessException for " + + o.getClass() + " " + name + "=" + value + ")", iae); + } catch (InvocationTargetException ie) { + Throwable cause = ie.getCause(); + if (cause instanceof ThreadDeath) { + throw (ThreadDeath) cause; + } + if (cause instanceof VirtualMachineError) { + throw (VirtualMachineError) cause; + } + if (log.isDebugEnabled()) + log.debug("IntrospectionUtils: InvocationTargetException for " + + o.getClass() + " " + name + "=" + value + ")", ie); + } + return false; + } + + public static String capitalize(String name) { + if (name == null || name.length() == 0) { + return name; + } + char chars[] = name.toCharArray(); + chars[0] = Character.toUpperCase(chars[0]); + return new String(chars); + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ClassLoaderUtil.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ClassLoaderUtil.java new file mode 100644 index 0000000..23b5668 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ClassLoaderUtil.java @@ -0,0 +1,81 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +public class ClassLoaderUtil { + private static final Log log = LogFactory.getLog(ClassLoaderUtil.class); + + private static final boolean onlyAttemptFirstLoader = + Boolean.parseBoolean(System.getProperty("org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader", "false")); + + public static Class loadClass(String className, ClassLoader... classLoaders) throws ClassNotFoundException { + ClassNotFoundException last = null; + StringBuilder errorMsg = null; + for (ClassLoader cl : classLoaders) { + try { + if (cl!=null) { + if (log.isDebugEnabled()) { + log.debug("Attempting to load class["+className+"] from "+cl); + } + return Class.forName(className, true, cl); + } else { + throw new ClassNotFoundException("Classloader is null"); + } + } catch (ClassNotFoundException x) { + last = x; + if (errorMsg==null) { + errorMsg = new StringBuilder(); + } else { + errorMsg.append(';'); + } + errorMsg.append("ClassLoader:"); + errorMsg.append(cl); + } + if (onlyAttemptFirstLoader) { + break; + } + } + throw new ClassNotFoundException("Unable to load class: "+className+" from "+errorMsg, last); + } + + + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java new file mode 100644 index 0000000..7b081df --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java @@ -0,0 +1,1500 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +/** + * Implementation of simple connection pool. + * The ConnectionPool uses a {@link PoolProperties} object for storing all the meta information about the connection pool. + * As the underlying implementation, the connection pool uses {@link java.util.concurrent.BlockingQueue} to store active and idle connections. + * A custom implementation of a fair {@link FairBlockingQueue} blocking queue is provided with the connection pool itself. + * @version 1.0 + */ +public class ConnectionPool { + + /** + * Default domain for objects registering with an mbean server + */ + public static final String POOL_JMX_DOMAIN = "tomcat.jdbc"; + /** + * Prefix type for JMX registration + */ + public static final String POOL_JMX_TYPE_PREFIX = POOL_JMX_DOMAIN+":type="; + + /** + * Logger + */ + private static final Log log = LogFactory.getLog(ConnectionPool.class); + + //=============================================================================== + // INSTANCE/QUICK ACCESS VARIABLE + //=============================================================================== + /** + * Carries the size of the pool, instead of relying on a queue implementation + * that usually iterates over to get an exact count + */ + private AtomicInteger size = new AtomicInteger(0); + + /** + * All the information about the connection pool + * These are the properties the pool got instantiated with + */ + private PoolConfiguration poolProperties; + + /** + * Contains all the connections that are in use + * TODO - this shouldn't be a blocking queue, simply a list to hold our objects + */ + private BlockingQueue busy; + + /** + * Contains all the idle connections + */ + private BlockingQueue idle; + + /** + * The thread that is responsible for checking abandoned and idle threads + */ + private volatile PoolCleaner poolCleaner; + + /** + * Pool closed flag + */ + private volatile boolean closed = false; + + /** + * Since newProxyInstance performs the same operation, over and over + * again, it is much more optimized if we simply store the constructor ourselves. + */ + private Constructor proxyClassConstructor; + + /** + * Executor service used to cancel Futures + */ + private ThreadPoolExecutor cancellator = new ThreadPoolExecutor(0,1,1000,TimeUnit.MILLISECONDS,new LinkedBlockingQueue()); + + /** + * reference to the JMX mbean + */ + protected org.apache.tomcat.jdbc.pool.jmx.ConnectionPool jmxPool = null; + + /** + * counter to track how many threads are waiting for a connection + */ + private AtomicInteger waitcount = new AtomicInteger(0); + + private AtomicLong poolVersion = new AtomicLong(Long.MIN_VALUE); + + /** + * The counters for statistics of the pool. + */ + private final AtomicLong borrowedCount = new AtomicLong(0); + private final AtomicLong returnedCount = new AtomicLong(0); + private final AtomicLong createdCount = new AtomicLong(0); + private final AtomicLong releasedCount = new AtomicLong(0); + private final AtomicLong reconnectedCount = new AtomicLong(0); + private final AtomicLong removeAbandonedCount = new AtomicLong(0); + private final AtomicLong releasedIdleCount = new AtomicLong(0); + + //=============================================================================== + // PUBLIC METHODS + //=============================================================================== + + /** + * Instantiate a connection pool. This will create connections if initialSize is larger than 0. + * The {@link PoolProperties} should not be reused for another connection pool. + * @param prop PoolProperties - all the properties for this connection pool + * @throws SQLException Pool initialization error + */ + public ConnectionPool(PoolConfiguration prop) throws SQLException { + //setup quick access variables and pools + init(prop); + } + + + /** + * Retrieves a Connection future. If a connection is not available, one can block using future.get() + * until a connection has become available. + * If a connection is not retrieved, the Future must be cancelled in order for the connection to be returned + * to the pool. + * @return a Future containing a reference to the connection or the future connection + * @throws SQLException Cannot use asynchronous connect + */ + public Future getConnectionAsync() throws SQLException { + try { + PooledConnection pc = borrowConnection(0, null, null); + if (pc!=null) { + return new ConnectionFuture(pc); + } + }catch (SQLException x) { + if (x.getMessage().indexOf("NoWait")<0) { + throw x; + } + } + //we can only retrieve a future if the underlying queue supports it. + if (idle instanceof FairBlockingQueue) { + Future pcf = ((FairBlockingQueue)idle).pollAsync(); + return new ConnectionFuture(pcf); + } else if (idle instanceof MultiLockFairBlockingQueue) { + Future pcf = ((MultiLockFairBlockingQueue)idle).pollAsync(); + return new ConnectionFuture(pcf); + } else { + throw new SQLException("Connection pool is misconfigured, doesn't support async retrieval. Set the 'fair' property to 'true'"); + } + } + + /** + * Borrows a connection from the pool. If a connection is available (in the idle queue) or the pool has not reached + * {@link PoolProperties#maxActive maxActive} connections a connection is returned immediately. + * If no connection is available, the pool will attempt to fetch a connection for {@link PoolProperties#maxWait maxWait} milliseconds. + * @return Connection - a java.sql.Connection/javax.sql.PooledConnection reflection proxy, wrapping the underlying object. + * @throws SQLException - if the wait times out or a failure occurs creating a connection + */ + public Connection getConnection() throws SQLException { + //check out a connection + PooledConnection con = borrowConnection(-1,null,null); + return setupConnection(con); + } + + + /** + * Borrows a connection from the pool. If a connection is available (in the + * idle queue) or the pool has not reached {@link PoolProperties#maxActive + * maxActive} connections a connection is returned immediately. If no + * connection is available, the pool will attempt to fetch a connection for + * {@link PoolProperties#maxWait maxWait} milliseconds. + * @param username The user name to use for the connection + * @param password The password for the connection + * @return Connection - a java.sql.Connection/javax.sql.PooledConnection + * reflection proxy, wrapping the underlying object. + * @throws SQLException + * - if the wait times out or a failure occurs creating a + * connection + */ + public Connection getConnection(String username, String password) throws SQLException { + // check out a connection + PooledConnection con = borrowConnection(-1, username, password); + return setupConnection(con); + } + + /** + * Returns the name of this pool + * @return String - the name of the pool + */ + public String getName() { + return getPoolProperties().getPoolName(); + } + + /** + * Return the number of threads waiting for a connection + * @return number of threads waiting for a connection + */ + public int getWaitCount() { + return waitcount.get(); + } + + /** + * Returns the pool properties associated with this connection pool + * @return PoolProperties + * + */ + public PoolConfiguration getPoolProperties() { + return this.poolProperties; + } + + /** + * Returns the total size of this pool, this includes both busy and idle connections + * @return int - number of established connections to the database + */ + public int getSize() { + return size.get(); + } + + /** + * Returns the number of connections that are in use + * @return int - number of established connections that are being used by the application + */ + public int getActive() { + return busy.size(); + } + + /** + * Returns the number of idle connections + * @return int - number of established connections not being used + */ + public int getIdle() { + return idle.size(); + } + + /** + * Returns true if {@link #close close} has been called, and the connection pool is unusable + * @return boolean + */ + public boolean isClosed() { + return this.closed; + } + + //=============================================================================== + // PROTECTED METHODS + //=============================================================================== + + + /** + * configures a pooled connection as a proxy. + * This Proxy implements {@link java.sql.Connection} and {@link javax.sql.PooledConnection} interfaces. + * All calls on {@link java.sql.Connection} methods will be propagated down to the actual JDBC connection except for the + * {@link java.sql.Connection#close()} method. + * @param con a {@link PooledConnection} to wrap in a Proxy + * @return a {@link java.sql.Connection} object wrapping a pooled connection. + * @throws SQLException if an interceptor can't be configured, if the proxy can't be instantiated + */ + protected Connection setupConnection(PooledConnection con) throws SQLException { + //fetch previously cached interceptor proxy - one per connection + JdbcInterceptor handler = con.getHandler(); + if (handler==null) { + //build the proxy handler + handler = new ProxyConnection(this,con,getPoolProperties().isUseEquals()); + //set up the interceptor chain + PoolProperties.InterceptorDefinition[] proxies = getPoolProperties().getJdbcInterceptorsAsArray(); + for (int i=proxies.length-1; i>=0; i--) { + try { + //create a new instance + JdbcInterceptor interceptor = proxies[i].getInterceptorClass().newInstance(); + //configure properties + interceptor.setProperties(proxies[i].getProperties()); + //setup the chain + interceptor.setNext(handler); + //call reset + interceptor.reset(this, con); + //configure the last one to be held by the connection + handler = interceptor; + }catch(Exception x) { + SQLException sx = new SQLException("Unable to instantiate interceptor chain."); + sx.initCause(x); + throw sx; + } + } + //cache handler for the next iteration + con.setHandler(handler); + } else { + JdbcInterceptor next = handler; + //we have a cached handler, reset it + while (next!=null) { + next.reset(this, con); + next = next.getNext(); + } + } + + try { + getProxyConstructor(con.getXAConnection() != null); + //create the proxy + //TODO possible optimization, keep track if this connection was returned properly, and don't generate a new facade + Connection connection = null; + if (getPoolProperties().getUseDisposableConnectionFacade() ) { + connection = (Connection)proxyClassConstructor.newInstance(new Object[] { new DisposableConnectionFacade(handler) }); + } else { + connection = (Connection)proxyClassConstructor.newInstance(new Object[] {handler}); + } + //return the connection + return connection; + }catch (Exception x) { + SQLException s = new SQLException(); + s.initCause(x); + throw s; + } + + } + + /** + * Creates and caches a {@link java.lang.reflect.Constructor} used to instantiate the proxy object. + * We cache this, since the creation of a constructor is fairly slow. + * @param xa Use a XA connection + * @return constructor used to instantiate the wrapper object + * @throws NoSuchMethodException Failed to get a constructor + */ + public Constructor getProxyConstructor(boolean xa) throws NoSuchMethodException { + //cache the constructor + if (proxyClassConstructor == null ) { + Class proxyClass = xa ? + Proxy.getProxyClass(ConnectionPool.class.getClassLoader(), new Class[] {java.sql.Connection.class,javax.sql.PooledConnection.class, javax.sql.XAConnection.class}) : + Proxy.getProxyClass(ConnectionPool.class.getClassLoader(), new Class[] {java.sql.Connection.class,javax.sql.PooledConnection.class}); + proxyClassConstructor = proxyClass.getConstructor(new Class[] { InvocationHandler.class }); + } + return proxyClassConstructor; + } + + /** + * Closes the pool and all disconnects all idle connections + * Active connections will be closed upon the {@link java.sql.Connection#close close} method is called + * on the underlying connection instead of being returned to the pool + * @param force - true to even close the active connections + */ + protected void close(boolean force) { + //are we already closed + if (this.closed) return; + //prevent other threads from entering + this.closed = true; + //stop background thread + if (poolCleaner!=null) { + poolCleaner.stopRunning(); + } + + /* release all idle connections */ + BlockingQueue pool = (idle.size()>0)?idle:(force?busy:idle); + while (pool.size()>0) { + try { + //retrieve the next connection + PooledConnection con = pool.poll(1000, TimeUnit.MILLISECONDS); + //close it and retrieve the next one, if one is available + while (con != null) { + //close the connection + if (pool==idle) + release(con); + else + abandon(con); + if (pool.size()>0) { + con = pool.poll(1000, TimeUnit.MILLISECONDS); + } else { + break; + } + } //while + } catch (InterruptedException ex) { + if (getPoolProperties().getPropagateInterruptState()) { + Thread.currentThread().interrupt(); + } + } + if (pool.size()==0 && force && pool!=busy) pool = busy; + } + if (this.getPoolProperties().isJmxEnabled()) this.jmxPool = null; + PoolProperties.InterceptorDefinition[] proxies = getPoolProperties().getJdbcInterceptorsAsArray(); + for (int i=0; i(); + //busy = new FairBlockingQueue(); + //make space for 10 extra in case we flow over a bit + if (properties.isFairQueue()) { + idle = new FairBlockingQueue<>(); + //idle = new MultiLockFairBlockingQueue(); + //idle = new LinkedTransferQueue(); + //idle = new ArrayBlockingQueue(properties.getMaxActive(),false); + } else { + idle = new LinkedBlockingQueue<>(); + } + + initializePoolCleaner(properties); + + //create JMX MBean + if (this.getPoolProperties().isJmxEnabled()) createMBean(); + + //Parse and create an initial set of interceptors. Letting them know the pool has started. + //These interceptors will not get any connection. + PoolProperties.InterceptorDefinition[] proxies = getPoolProperties().getJdbcInterceptorsAsArray(); + for (int i=0; iproperties.getMaxActive()) { + log.warn("minIdle is larger than maxActive, setting minIdle to: "+properties.getMaxActive()); + properties.setMinIdle(properties.getMaxActive()); + } + if (properties.getMaxIdle()>properties.getMaxActive()) { + log.warn("maxIdle is larger than maxActive, setting maxIdle to: "+properties.getMaxActive()); + properties.setMaxIdle(properties.getMaxActive()); + } + if (properties.getMaxIdle() 0) { + idle.offer(create(true)); + } + } + + /** + * Thread safe way to retrieve a connection from the pool + * @param wait - time to wait, overrides the maxWait from the properties, + * set to -1 if you wish to use maxWait, 0 if you wish no wait time. + * @param username The user name to use for the connection + * @param password The password for the connection + * @return a connection + * @throws SQLException Failed to get a connection + */ + private PooledConnection borrowConnection(int wait, String username, String password) throws SQLException { + + if (isClosed()) { + throw new SQLException("Connection pool closed."); + } //end if + + //get the current time stamp + long now = System.currentTimeMillis(); + //see if there is one available immediately + PooledConnection con = idle.poll(); + + while (true) { + if (con!=null) { + //configure the connection and return it + PooledConnection result = borrowConnection(now, con, username, password); + borrowedCount.incrementAndGet(); + if (result!=null) return result; + } + + //if we get here, see if we need to create one + //this is not 100% accurate since it doesn't use a shared + //atomic variable - a connection can become idle while we are creating + //a new connection + if (size.get() < getPoolProperties().getMaxActive()) { + //atomic duplicate check + if (size.addAndGet(1) > getPoolProperties().getMaxActive()) { + //if we got here, two threads passed through the first if + size.decrementAndGet(); + } else { + //create a connection, we're below the limit + return createConnection(now, con, username, password); + } + } //end if + + //calculate wait time for this iteration + long maxWait = wait; + //if the passed in wait time is -1, means we should use the pool property value + if (wait==-1) { + maxWait = (getPoolProperties().getMaxWait()<=0)?Long.MAX_VALUE:getPoolProperties().getMaxWait(); + } + + long timetowait = Math.max(0, maxWait - (System.currentTimeMillis() - now)); + waitcount.incrementAndGet(); + try { + //retrieve an existing connection + con = idle.poll(timetowait, TimeUnit.MILLISECONDS); + } catch (InterruptedException ex) { + if (getPoolProperties().getPropagateInterruptState()) { + Thread.currentThread().interrupt(); + } + SQLException sx = new SQLException("Pool wait interrupted."); + sx.initCause(ex); + throw sx; + } finally { + waitcount.decrementAndGet(); + } + if (maxWait==0 && con == null) { //no wait, return one if we have one + if (jmxPool!=null) { + jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - no wait."); + } + throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " + + "NoWait: Pool empty. Unable to fetch a connection, none available["+busy.size()+" in use]."); + } + //we didn't get a connection, lets see if we timed out + if (con == null) { + if ((System.currentTimeMillis() - now) >= maxWait) { + if (jmxPool!=null) { + jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - timeout."); + } + throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " + + "Timeout: Pool empty. Unable to fetch a connection in " + (maxWait / 1000) + + " seconds, none available[size:"+size.get() +"; busy:"+busy.size()+"; idle:"+idle.size()+"; lastwait:"+timetowait+"]."); + } else { + //no timeout, lets try again + continue; + } + } + } //while + } + + /** + * Creates a JDBC connection and tries to connect to the database. + * @param now timestamp of when this was called + * @param notUsed Argument not used + * @param username The user name to use for the connection + * @param password The password for the connection + * @return a PooledConnection that has been connected + * @throws SQLException Failed to get a connection + */ + protected PooledConnection createConnection(long now, PooledConnection notUsed, String username, String password) throws SQLException { + //no connections where available we'll create one + PooledConnection con = create(false); + if (username!=null) con.getAttributes().put(PooledConnection.PROP_USER, username); + if (password!=null) con.getAttributes().put(PooledConnection.PROP_PASSWORD, password); + boolean error = false; + try { + //connect and validate the connection + con.lock(); + con.connect(); + if (con.validate(PooledConnection.VALIDATE_INIT)) { + //no need to lock a new one, its not contented + con.setTimestamp(now); + if (getPoolProperties().isLogAbandoned()) { + con.setStackTrace(getThreadDump()); + } + if (!busy.offer(con)) { + log.debug("Connection doesn't fit into busy array, connection will not be traceable."); + } + createdCount.incrementAndGet(); + return con; + } else { + //validation failed, make sure we disconnect + //and clean up + throw new SQLException("Validation Query Failed, enable logValidationErrors for more details."); + } //end if + } catch (Exception e) { + error = true; + if (log.isDebugEnabled()) + log.debug("Unable to create a new JDBC connection.", e); + if (e instanceof SQLException) { + throw (SQLException)e; + } else { + SQLException ex = new SQLException(e.getMessage()); + ex.initCause(e); + throw ex; + } + } finally { + // con can never be null here + if (error ) { + release(con); + } + con.unlock(); + }//catch + } + + /** + * Validates and configures a previously idle connection + * @param now - timestamp + * @param con - the connection to validate and configure + * @param username The user name to use for the connection + * @param password The password for the connection + * @return a connection + * @throws SQLException if a validation error happens + */ + protected PooledConnection borrowConnection(long now, PooledConnection con, String username, String password) throws SQLException { + //we have a connection, lets set it up + + //flag to see if we need to nullify + boolean setToNull = false; + try { + con.lock(); + if (con.isReleased()) { + return null; + } + + //evaluate username/password change as well as max age functionality + boolean forceReconnect = con.shouldForceReconnect(username, password) || con.isMaxAgeExpired(); + + if (!con.isDiscarded() && !con.isInitialized()) { + //here it states that the connection not discarded, but the connection is null + //don't attempt a connect here. It will be done during the reconnect. + forceReconnect = true; + } + + if (!forceReconnect) { + if ((!con.isDiscarded()) && con.validate(PooledConnection.VALIDATE_BORROW)) { + //set the timestamp + con.setTimestamp(now); + if (getPoolProperties().isLogAbandoned()) { + //set the stack trace for this pool + con.setStackTrace(getThreadDump()); + } + if (!busy.offer(con)) { + log.debug("Connection doesn't fit into busy array, connection will not be traceable."); + } + return con; + } + } + //if we reached here, that means the connection + //is either has another principal, is discarded or validation failed. + //we will make one more attempt + //in order to guarantee that the thread that just acquired + //the connection shouldn't have to poll again. + try { + con.reconnect(); + reconnectedCount.incrementAndGet(); + int validationMode = getPoolProperties().isTestOnConnect() || getPoolProperties().getInitSQL()!=null ? + PooledConnection.VALIDATE_INIT : + PooledConnection.VALIDATE_BORROW; + + if (con.validate(validationMode)) { + //set the timestamp + con.setTimestamp(now); + if (getPoolProperties().isLogAbandoned()) { + //set the stack trace for this pool + con.setStackTrace(getThreadDump()); + } + if (!busy.offer(con)) { + log.debug("Connection doesn't fit into busy array, connection will not be traceable."); + } + return con; + } else { + //validation failed. + throw new SQLException("Failed to validate a newly established connection."); + } + } catch (Exception x) { + release(con); + setToNull = true; + if (x instanceof SQLException) { + throw (SQLException)x; + } else { + SQLException ex = new SQLException(x.getMessage()); + ex.initCause(x); + throw ex; + } + } + } finally { + con.unlock(); + if (setToNull) { + con = null; + } + } + } + /** + * Terminate the current transaction for the given connection. + * @param con The connection + * @return true if the connection TX termination succeeded + * otherwise false + */ + protected boolean terminateTransaction(PooledConnection con) { + try { + if (Boolean.FALSE.equals(con.getPoolProperties().getDefaultAutoCommit())) { + if (this.getPoolProperties().getRollbackOnReturn()) { + boolean autocommit = con.getConnection().getAutoCommit(); + if (!autocommit) con.getConnection().rollback(); + } else if (this.getPoolProperties().getCommitOnReturn()) { + boolean autocommit = con.getConnection().getAutoCommit(); + if (!autocommit) con.getConnection().commit(); + } + } + return true; + } catch (SQLException x) { + log.warn("Unable to terminate transaction, connection will be closed.",x); + return false; + } + + } + + /** + * Determines if a connection should be closed upon return to the pool. + * @param con - the connection + * @param action - the validation action that should be performed + * @return true if the connection should be closed + */ + protected boolean shouldClose(PooledConnection con, int action) { + if (con.getConnectionVersion() < getPoolVersion()) return true; + if (con.isDiscarded()) return true; + if (isClosed()) return true; + if (!con.validate(action)) return true; + if (!terminateTransaction(con)) return true; + if (con.isMaxAgeExpired()) return true; + else return false; + } + + /** + * Returns a connection to the pool + * If the pool is closed, the connection will be released + * If the connection is not part of the busy queue, it will be released. + * If {@link PoolProperties#testOnReturn} is set to true it will be validated + * @param con PooledConnection to be returned to the pool + */ + protected void returnConnection(PooledConnection con) { + if (isClosed()) { + //if the connection pool is closed + //close the connection instead of returning it + release(con); + return; + } //end if + + if (con != null) { + try { + returnedCount.incrementAndGet(); + con.lock(); + if (con.isSuspect()) { + if (poolProperties.isLogAbandoned() && log.isInfoEnabled()) { + log.info("Connection(" + con + ") that has been marked suspect was returned." + + " The processing time is " + (System.currentTimeMillis()-con.getTimestamp()) + " ms."); + } + if (jmxPool!=null) { + jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.SUSPECT_RETURNED_NOTIFICATION, + "Connection(" + con + ") that has been marked suspect was returned."); + } + } + if (busy.remove(con)) { + + if (!shouldClose(con,PooledConnection.VALIDATE_RETURN)) { + con.setStackTrace(null); + con.setTimestamp(System.currentTimeMillis()); + if (((idle.size()>=poolProperties.getMaxIdle()) && !poolProperties.isPoolSweeperEnabled()) || (!idle.offer(con))) { + if (log.isDebugEnabled()) { + log.debug("Connection ["+con+"] will be closed and not returned to the pool, idle["+idle.size()+"]>=maxIdle["+poolProperties.getMaxIdle()+"] idle.offer failed."); + } + release(con); + } + } else { + if (log.isDebugEnabled()) { + log.debug("Connection ["+con+"] will be closed and not returned to the pool."); + } + release(con); + } //end if + } else { + if (log.isDebugEnabled()) { + log.debug("Connection ["+con+"] will be closed and not returned to the pool, busy.remove failed."); + } + release(con); + } + } finally { + con.unlock(); + } + } //end if + } //checkIn + + /** + * Determines if a connection should be abandoned based on + * {@link PoolProperties#abandonWhenPercentageFull} setting. + * @return true if the connection should be abandoned + */ + protected boolean shouldAbandon() { + if (!poolProperties.isRemoveAbandoned()) return false; + if (poolProperties.getAbandonWhenPercentageFull()==0) return true; + float used = busy.size(); + float max = poolProperties.getMaxActive(); + float perc = poolProperties.getAbandonWhenPercentageFull(); + return (used/max*100f)>=perc; + } + + /** + * Iterates through all the busy connections and checks for connections that have timed out + */ + public void checkAbandoned() { + try { + if (busy.size()==0) return; + Iterator locked = busy.iterator(); + int sto = getPoolProperties().getSuspectTimeout(); + while (locked.hasNext()) { + PooledConnection con = locked.next(); + boolean setToNull = false; + try { + con.lock(); + //the con has been returned to the pool or released + //ignore it + if (idle.contains(con) || con.isReleased()) + continue; + long time = con.getTimestamp(); + long now = System.currentTimeMillis(); + if (shouldAbandon() && (now - time) > con.getAbandonTimeout()) { + busy.remove(con); + abandon(con); + setToNull = true; + } else if (sto > 0 && (now - time) > (sto * 1000L)) { + suspect(con); + } else { + //do nothing + } //end if + } finally { + con.unlock(); + if (setToNull) + con = null; + } + } //while + } catch (ConcurrentModificationException e) { + log.debug("checkAbandoned failed." ,e); + } catch (Exception e) { + log.warn("checkAbandoned failed, it will be retried.",e); + } + } + + /** + * Iterates through the idle connections and resizes the idle pool based on parameters + * {@link PoolProperties#maxIdle}, {@link PoolProperties#minIdle}, {@link PoolProperties#minEvictableIdleTimeMillis} + */ + public void checkIdle() { + checkIdle(false); + } + + public void checkIdle(boolean ignoreMinSize) { + + try { + if (idle.size()==0) return; + long now = System.currentTimeMillis(); + Iterator unlocked = idle.iterator(); + while ( (ignoreMinSize || (idle.size()>=getPoolProperties().getMinIdle())) && unlocked.hasNext()) { + PooledConnection con = unlocked.next(); + boolean setToNull = false; + try { + con.lock(); + //the con been taken out, we can't clean it up + if (busy.contains(con)) + continue; + long time = con.getTimestamp(); + if (shouldReleaseIdle(now, con, time)) { + releasedIdleCount.incrementAndGet(); + release(con); + idle.remove(con); + setToNull = true; + } else { + //do nothing + } //end if + } finally { + con.unlock(); + if (setToNull) + con = null; + } + } //while + } catch (ConcurrentModificationException e) { + log.debug("checkIdle failed." ,e); + } catch (Exception e) { + log.warn("checkIdle failed, it will be retried.",e); + } + + } + + + protected boolean shouldReleaseIdle(long now, PooledConnection con, long time) { + if (con.getConnectionVersion() < getPoolVersion()) return true; + else return (con.getReleaseTime()>0) && ((now - time) > con.getReleaseTime()) && (getSize()>getPoolProperties().getMinIdle()); + } + + /** + * Forces a validation of all idle connections if {@link PoolProperties#testWhileIdle} is set. + */ + public void testAllIdle() { + try { + if (idle.size()==0) return; + Iterator unlocked = idle.iterator(); + while (unlocked.hasNext()) { + PooledConnection con = unlocked.next(); + try { + con.lock(); + //the con been taken out, we can't clean it up + if (busy.contains(con)) + continue; + if (!con.validate(PooledConnection.VALIDATE_IDLE)) { + idle.remove(con); + release(con); + } + } finally { + con.unlock(); + } + } //while + } catch (ConcurrentModificationException e) { + log.debug("testAllIdle failed." ,e); + } catch (Exception e) { + log.warn("testAllIdle failed, it will be retried.",e); + } + + } + + /** + * Creates a stack trace representing the existing thread's current state. + * @return a string object representing the current state. + * TODO investigate if we simply should store {@link java.lang.Thread#getStackTrace()} elements + */ + protected static String getThreadDump() { + Exception x = new Exception(); + x.fillInStackTrace(); + return getStackTrace(x); + } + + /** + * Convert an exception into a String + * @param x - the throwable + * @return a string representing the stack trace + */ + public static String getStackTrace(Throwable x) { + if (x == null) { + return null; + } else { + java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream(); + java.io.PrintStream writer = new java.io.PrintStream(bout); + x.printStackTrace(writer); + String result = bout.toString(); + return (x.getMessage()!=null && x.getMessage().length()>0)? x.getMessage()+";"+result:result; + } //end if + } + + + /** + * Create a new pooled connection object. Not connected nor validated. + * @param incrementCounter true to increment the connection count + * @return a pooled connection object + */ + protected PooledConnection create(boolean incrementCounter) { + if (incrementCounter) size.incrementAndGet(); + PooledConnection con = new PooledConnection(getPoolProperties(), this); + return con; + } + + /** + * Purges all connections in the pool. + * For connections currently in use, these connections will be + * purged when returned on the pool. This call also + * purges connections that are idle and in the pool + * To only purge used/active connections see {@link #purgeOnReturn()} + */ + public void purge() { + purgeOnReturn(); + checkIdle(true); + } + + /** + * Purges connections when they are returned from the pool. + * This call does not purge idle connections until they are used. + * To purge idle connections see {@link #purge()} + */ + public void purgeOnReturn() { + poolVersion.incrementAndGet(); + } + + /** + * Hook to perform final actions on a pooled connection object once it has been disconnected and will be discarded + * @param con The connection + */ + protected void finalize(PooledConnection con) { + JdbcInterceptor handler = con.getHandler(); + while (handler!=null) { + handler.reset(null, null); + handler=handler.getNext(); + } + } + + /** + * Hook to perform final actions on a pooled connection object once it has been disconnected and will be discarded + * @param con The connection + * @param finalizing true if finalizing the connection + */ + protected void disconnectEvent(PooledConnection con, boolean finalizing) { + JdbcInterceptor handler = con.getHandler(); + while (handler!=null) { + handler.disconnected(this, con, finalizing); + handler=handler.getNext(); + } + } + + /** + * Return the object that is potentially registered in JMX for notifications + * @return the object implementing the {@link org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean} interface + */ + public org.apache.tomcat.jdbc.pool.jmx.ConnectionPool getJmxPool() { + return jmxPool; + } + + /** + * Create MBean object that can be registered. + */ + protected void createMBean() { + try { + jmxPool = new org.apache.tomcat.jdbc.pool.jmx.ConnectionPool(this); + } catch (Exception x) { + log.warn("Unable to start JMX integration for connection pool. Instance["+getName()+"] can't be monitored.",x); + } + } + + /** + * The total number of connections borrowed from this pool. + * @return the borrowed connection count + */ + public long getBorrowedCount() { + return borrowedCount.get(); + } + + /** + * The total number of connections returned to this pool. + * @return the returned connection count + */ + public long getReturnedCount() { + return returnedCount.get(); + } + + /** + * The total number of connections created by this pool. + * @return the created connection count + */ + public long getCreatedCount() { + return createdCount.get(); + } + + /** + * The total number of connections released from this pool. + * @return the released connection count + */ + public long getReleasedCount() { + return releasedCount.get(); + } + + /** + * The total number of connections reconnected by this pool. + * @return the reconnected connection count + */ + public long getReconnectedCount() { + return reconnectedCount.get(); + } + + /** + * The total number of connections released by remove abandoned. + * @return the PoolCleaner removed abandoned connection count + */ + public long getRemoveAbandonedCount() { + return removeAbandonedCount.get(); + } + + /** + * The total number of connections released by eviction. + * @return the PoolCleaner evicted idle connection count + */ + public long getReleasedIdleCount() { + return releasedIdleCount.get(); + } + + /** + * reset the statistics of this pool. + */ + public void resetStats() { + borrowedCount.set(0); + returnedCount.set(0); + createdCount.set(0); + releasedCount.set(0); + reconnectedCount.set(0); + removeAbandonedCount.set(0); + releasedIdleCount.set(0); + } + + /** + * Tread safe wrapper around a future for the regular queue + * This one retrieves the pooled connection object + * and performs the initialization according to + * interceptors and validation rules. + * This class is thread safe and is cancellable + * + */ + protected class ConnectionFuture implements Future, Runnable { + Future pcFuture = null; + AtomicBoolean configured = new AtomicBoolean(false); + CountDownLatch latch = new CountDownLatch(1); + volatile Connection result = null; + SQLException cause = null; + AtomicBoolean cancelled = new AtomicBoolean(false); + volatile PooledConnection pc = null; + public ConnectionFuture(Future pcf) { + this.pcFuture = pcf; + } + + public ConnectionFuture(PooledConnection pc) throws SQLException { + this.pc = pc; + result = ConnectionPool.this.setupConnection(pc); + configured.set(true); + } + /** + * {@inheritDoc} + */ + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + if (pc!=null) { + return false; + } else if ((!cancelled.get()) && cancelled.compareAndSet(false, true)) { + //cancel by retrieving the connection and returning it to the pool + ConnectionPool.this.cancellator.execute(this); + } + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public Connection get() throws InterruptedException, ExecutionException { + try { + return get(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + }catch (TimeoutException x) { + throw new ExecutionException(x); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Connection get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + PooledConnection pc = this.pc!=null?this.pc:pcFuture.get(timeout,unit); + if (pc!=null) { + if (result!=null) return result; + if (configured.compareAndSet(false, true)) { + try { + pc = borrowConnection(System.currentTimeMillis(),pc, null, null); + result = ConnectionPool.this.setupConnection(pc); + } catch (SQLException x) { + cause = x; + } finally { + latch.countDown(); + } + } else { + //if we reach here, another thread is configuring the actual connection + latch.await(timeout,unit); //this shouldn't block for long + } + if (result==null) throw new ExecutionException(cause); + return result; + } else { + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCancelled() { + return pc==null && (pcFuture.isCancelled() || cancelled.get()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isDone() { + return pc!=null || pcFuture.isDone(); + } + + /** + * run method to be executed when cancelled by an executor + */ + @Override + public void run() { + try { + Connection con = get(); //complete this future + con.close(); //return to the pool + }catch (ExecutionException ex) { + //we can ignore this + }catch (Exception x) { + ConnectionPool.log.error("Unable to cancel ConnectionFuture.",x); + } + } + + } + + + + private static volatile Timer poolCleanTimer = null; + private static HashSet cleaners = new HashSet<>(); + + private static synchronized void registerCleaner(PoolCleaner cleaner) { + unregisterCleaner(cleaner); + cleaners.add(cleaner); + if (poolCleanTimer == null) { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(ConnectionPool.class.getClassLoader()); + // Create the timer thread in a PrivilegedAction so that a + // reference to the web application class loader is not created + // via Thread.inheritedAccessControlContext + PrivilegedAction pa = new PrivilegedNewTimer(); + poolCleanTimer = AccessController.doPrivileged(pa); + } finally { + Thread.currentThread().setContextClassLoader(loader); + } + } + poolCleanTimer.schedule(cleaner, cleaner.sleepTime,cleaner.sleepTime); + } + + private static synchronized void unregisterCleaner(PoolCleaner cleaner) { + boolean removed = cleaners.remove(cleaner); + if (removed) { + cleaner.cancel(); + if (poolCleanTimer != null) { + poolCleanTimer.purge(); + if (cleaners.size() == 0) { + poolCleanTimer.cancel(); + poolCleanTimer = null; + } + } + } + } + + private static class PrivilegedNewTimer implements PrivilegedAction { + @Override + public Timer run() { + return new Timer("Tomcat JDBC Pool Cleaner["+ System.identityHashCode(ConnectionPool.class.getClassLoader()) + ":"+ + System.currentTimeMillis() + "]", true); + } + } + + public static Set getPoolCleaners() { + return Collections.unmodifiableSet(cleaners); + } + + public long getPoolVersion() { + return poolVersion.get(); + } + + public static Timer getPoolTimer() { + return poolCleanTimer; + } + + protected static class PoolCleaner extends TimerTask { + protected WeakReference pool; + protected long sleepTime; + + PoolCleaner(ConnectionPool pool, long sleepTime) { + this.pool = new WeakReference<>(pool); + this.sleepTime = sleepTime; + if (sleepTime <= 0) { + log.warn("Database connection pool evicter thread interval is set to 0, defaulting to 30 seconds"); + this.sleepTime = 1000 * 30; + } else if (sleepTime < 1000) { + log.warn("Database connection pool evicter thread interval is set to lower than 1 second."); + } + } + + @Override + public void run() { + ConnectionPool pool = this.pool.get(); + if (pool == null) { + stopRunning(); + } else if (!pool.isClosed()) { + try { + if (pool.getPoolProperties().isRemoveAbandoned() + || pool.getPoolProperties().getSuspectTimeout() > 0) + pool.checkAbandoned(); + if (pool.getPoolProperties().getMinIdle() < pool.idle + .size()) + pool.checkIdle(); + if (pool.getPoolProperties().isTestWhileIdle()) + pool.testAllIdle(); + } catch (Exception x) { + log.error("", x); + } + } + } + + public void start() { + registerCleaner(this); + } + + public void stopRunning() { + unregisterCleaner(this); + } + } +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java new file mode 100644 index 0000000..4922bf0 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java @@ -0,0 +1,180 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.lang.management.ManagementFactory; +import java.util.Hashtable; + +import javax.management.InstanceNotFoundException; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + + +/** + * A DataSource that can be instantiated through IoC and implements the DataSource interface + * since the DataSourceProxy is used as a generic proxy. + * The DataSource simply wraps a {@link ConnectionPool} in order to provide a standard interface to the user. + * @version 1.0 + */ +public class DataSource extends DataSourceProxy implements javax.sql.DataSource,MBeanRegistration, org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean, javax.sql.ConnectionPoolDataSource { + private static final Log log = LogFactory.getLog(DataSource.class); + + /** + * Constructor for reflection only. A default set of pool properties will be created. + */ + public DataSource() { + super(); + } + + /** + * Constructs a DataSource object wrapping a connection + * @param poolProperties The pool properties + */ + public DataSource(PoolConfiguration poolProperties) { + super(poolProperties); + } + + + + + +//=============================================================================== +// JMX Operations - Register the actual pool itself under the tomcat.jdbc domain +//=============================================================================== + protected volatile ObjectName oname = null; + + /** + * Unregisters the underlying connection pool mbean.
+ * {@inheritDoc} + */ + @Override + public void postDeregister() { + if (oname!=null) unregisterJmx(); + } + + /** + * no-op
+ * {@inheritDoc} + */ + @Override + public void postRegister(Boolean registrationDone) { + // NOOP + } + + + /** + * no-op
+ * {@inheritDoc} + */ + @Override + public void preDeregister() throws Exception { + // NOOP + } + + /** + * If the connection pool MBean exists, it will be registered during this operation.
+ * {@inheritDoc} + */ + @Override + public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { + try { + if ( isJmxEnabled() ) { + this.oname = createObjectName(name); + if (oname!=null) registerJmx(); + } + }catch (MalformedObjectNameException x) { + log.error("Unable to create object name for JDBC pool.",x); + } + return name; + } + + /** + * Creates the ObjectName for the ConnectionPoolMBean object to be registered + * @param original the ObjectName for the DataSource + * @return the ObjectName for the ConnectionPoolMBean + * @throws MalformedObjectNameException Invalid object name + */ + public ObjectName createObjectName(ObjectName original) throws MalformedObjectNameException { + String domain = ConnectionPool.POOL_JMX_DOMAIN; + Hashtable properties = original.getKeyPropertyList(); + String origDomain = original.getDomain(); + properties.put("type", "ConnectionPool"); + properties.put("class", this.getClass().getName()); + if (original.getKeyProperty("path")!=null || properties.get("context")!=null) { + //this ensures that if the registration came from tomcat, we're not losing + //the unique domain, but putting that into as an engine attribute + properties.put("engine", origDomain); + } + ObjectName name = new ObjectName(domain,properties); + return name; + } + + /** + * Registers the ConnectionPoolMBean under a unique name based on the ObjectName for the DataSource + */ + protected void registerJmx() { + try { + if (pool.getJmxPool()!=null) { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + mbs.registerMBean(pool.getJmxPool(), oname); + } + } catch (Exception e) { + log.error("Unable to register JDBC pool with JMX",e); + } + } + + /** + * + */ + protected void unregisterJmx() { + try { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + mbs.unregisterMBean(oname); + } catch (InstanceNotFoundException ignore) { + // NOOP + } catch (Exception e) { + log.error("Unable to unregister JDBC pool with JMX",e); + } + } + + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java new file mode 100644 index 0000000..d0a5127 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java @@ -0,0 +1,612 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + + +import java.sql.Connection; +import java.util.Hashtable; +import java.util.Properties; + +import javax.management.ObjectName; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.spi.ObjectFactory; +import javax.sql.DataSource; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +/** + *

JNDI object factory that creates an instance of + * BasicDataSource that has been configured based on the + * RefAddr values of the specified Reference, + * which must match the names and data types of the + * BasicDataSource bean properties.

+ *
+ * Properties available for configuration:
+ * Commons DBCP properties
+ *
    + *
  1. initSQL - A query that gets executed once, right after the connection is established.
  2. + *
  3. testOnConnect - run validationQuery after connection has been established.
  4. + *
  5. validationInterval - avoid excess validation, only run validation at most at this frequency - time in milliseconds.
  6. + *
  7. jdbcInterceptors - a semicolon separated list of classnames extending {@link JdbcInterceptor} class.
  8. + *
  9. jmxEnabled - true of false, whether to register the pool with JMX.
  10. + *
  11. fairQueue - true of false, whether the pool should sacrifice a little bit of performance for true fairness.
  12. + *
+ * @author Craig R. McClanahan + * @author Dirk Verbeeck + */ +public class DataSourceFactory implements ObjectFactory { + private static final Log log = LogFactory.getLog(DataSourceFactory.class); + + protected static final String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit"; + protected static final String PROP_DEFAULTREADONLY = "defaultReadOnly"; + protected static final String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation"; + protected static final String PROP_DEFAULTCATALOG = "defaultCatalog"; + + protected static final String PROP_DRIVERCLASSNAME = "driverClassName"; + protected static final String PROP_PASSWORD = "password"; + protected static final String PROP_URL = "url"; + protected static final String PROP_USERNAME = "username"; + + protected static final String PROP_MAXACTIVE = "maxActive"; + protected static final String PROP_MAXIDLE = "maxIdle"; + protected static final String PROP_MINIDLE = "minIdle"; + protected static final String PROP_INITIALSIZE = "initialSize"; + protected static final String PROP_MAXWAIT = "maxWait"; + protected static final String PROP_MAXAGE = "maxAge"; + + protected static final String PROP_TESTONBORROW = "testOnBorrow"; + protected static final String PROP_TESTONRETURN = "testOnReturn"; + protected static final String PROP_TESTWHILEIDLE = "testWhileIdle"; + protected static final String PROP_TESTONCONNECT = "testOnConnect"; + protected static final String PROP_VALIDATIONQUERY = "validationQuery"; + protected static final String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout"; + protected static final String PROP_VALIDATOR_CLASS_NAME = "validatorClassName"; + + protected static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun"; + protected static final String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis"; + protected static final String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis"; + + protected static final String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed"; + + protected static final String PROP_REMOVEABANDONED = "removeAbandoned"; + protected static final String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout"; + protected static final String PROP_LOGABANDONED = "logAbandoned"; + protected static final String PROP_ABANDONWHENPERCENTAGEFULL = "abandonWhenPercentageFull"; + + protected static final String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements"; + protected static final String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements"; + protected static final String PROP_CONNECTIONPROPERTIES = "connectionProperties"; + + protected static final String PROP_INITSQL = "initSQL"; + protected static final String PROP_INTERCEPTORS = "jdbcInterceptors"; + protected static final String PROP_VALIDATIONINTERVAL = "validationInterval"; + protected static final String PROP_JMX_ENABLED = "jmxEnabled"; + protected static final String PROP_FAIR_QUEUE = "fairQueue"; + + protected static final String PROP_USE_EQUALS = "useEquals"; + protected static final String PROP_USE_CON_LOCK = "useLock"; + + protected static final String PROP_DATASOURCE= "dataSource"; + protected static final String PROP_DATASOURCE_JNDI = "dataSourceJNDI"; + + protected static final String PROP_SUSPECT_TIMEOUT = "suspectTimeout"; + + protected static final String PROP_ALTERNATE_USERNAME_ALLOWED = "alternateUsernameAllowed"; + + protected static final String PROP_COMMITONRETURN = "commitOnReturn"; + protected static final String PROP_ROLLBACKONRETURN = "rollbackOnReturn"; + + protected static final String PROP_USEDISPOSABLECONNECTIONFACADE = "useDisposableConnectionFacade"; + + protected static final String PROP_LOGVALIDATIONERRORS = "logValidationErrors"; + + protected static final String PROP_PROPAGATEINTERRUPTSTATE = "propagateInterruptState"; + + protected static final String PROP_IGNOREEXCEPTIONONPRELOAD = "ignoreExceptionOnPreLoad"; + + public static final int UNKNOWN_TRANSACTIONISOLATION = -1; + + public static final String OBJECT_NAME = "object_name"; + + + protected static final String[] ALL_PROPERTIES = { + PROP_DEFAULTAUTOCOMMIT, + PROP_DEFAULTREADONLY, + PROP_DEFAULTTRANSACTIONISOLATION, + PROP_DEFAULTCATALOG, + PROP_DRIVERCLASSNAME, + PROP_MAXACTIVE, + PROP_MAXIDLE, + PROP_MINIDLE, + PROP_INITIALSIZE, + PROP_MAXWAIT, + PROP_TESTONBORROW, + PROP_TESTONRETURN, + PROP_TIMEBETWEENEVICTIONRUNSMILLIS, + PROP_NUMTESTSPEREVICTIONRUN, + PROP_MINEVICTABLEIDLETIMEMILLIS, + PROP_TESTWHILEIDLE, + PROP_TESTONCONNECT, + PROP_PASSWORD, + PROP_URL, + PROP_USERNAME, + PROP_VALIDATIONQUERY, + PROP_VALIDATIONQUERY_TIMEOUT, + PROP_VALIDATOR_CLASS_NAME, + PROP_VALIDATIONINTERVAL, + PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED, + PROP_REMOVEABANDONED, + PROP_REMOVEABANDONEDTIMEOUT, + PROP_LOGABANDONED, + PROP_POOLPREPAREDSTATEMENTS, + PROP_MAXOPENPREPAREDSTATEMENTS, + PROP_CONNECTIONPROPERTIES, + PROP_INITSQL, + PROP_INTERCEPTORS, + PROP_JMX_ENABLED, + PROP_FAIR_QUEUE, + PROP_USE_EQUALS, + OBJECT_NAME, + PROP_ABANDONWHENPERCENTAGEFULL, + PROP_MAXAGE, + PROP_USE_CON_LOCK, + PROP_DATASOURCE, + PROP_DATASOURCE_JNDI, + PROP_SUSPECT_TIMEOUT, + PROP_ALTERNATE_USERNAME_ALLOWED, + PROP_COMMITONRETURN, + PROP_ROLLBACKONRETURN, + PROP_USEDISPOSABLECONNECTIONFACADE, + PROP_LOGVALIDATIONERRORS, + PROP_PROPAGATEINTERRUPTSTATE, + PROP_IGNOREEXCEPTIONONPRELOAD + }; + + // -------------------------------------------------- ObjectFactory Methods + + /** + *

Create and return a new BasicDataSource instance. If no + * instance can be created, return null instead.

+ * + * @param obj The possibly null object containing location or + * reference information that can be used in creating an object + * @param name The name of this object relative to nameCtx + * @param nameCtx The context relative to which the name + * parameter is specified, or null if name + * is relative to the default initial context + * @param environment The possibly null environment that is used in + * creating this object + * + * @exception Exception if an exception occurs creating the instance + */ + @Override + public Object getObjectInstance(Object obj, Name name, Context nameCtx, + Hashtable environment) throws Exception { + + // We only know how to deal with javax.naming.References + // that specify a class name of "javax.sql.DataSource" + if ((obj == null) || !(obj instanceof Reference)) { + return null; + } + Reference ref = (Reference) obj; + boolean XA = false; + boolean ok = false; + if ("javax.sql.DataSource".equals(ref.getClassName())) { + ok = true; + } + if ("javax.sql.XADataSource".equals(ref.getClassName())) { + ok = true; + XA = true; + } + if (org.apache.tomcat.jdbc.pool.DataSource.class.getName().equals(ref.getClassName())) { + ok = true; + } + + if (!ok) { + log.warn(ref.getClassName()+" is not a valid class name/type for this JNDI factory."); + return null; + } + + + Properties properties = new Properties(); + for (int i = 0; i < ALL_PROPERTIES.length; i++) { + String propertyName = ALL_PROPERTIES[i]; + RefAddr ra = ref.get(propertyName); + if (ra != null) { + String propertyValue = ra.getContent().toString(); + properties.setProperty(propertyName, propertyValue); + } + } + + return createDataSource(properties,nameCtx,XA); + } + + public static PoolConfiguration parsePoolProperties(Properties properties) { + PoolConfiguration poolProperties = new PoolProperties(); + String value = null; + + value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT); + if (value != null) { + poolProperties.setDefaultAutoCommit(Boolean.valueOf(value)); + } + + value = properties.getProperty(PROP_DEFAULTREADONLY); + if (value != null) { + poolProperties.setDefaultReadOnly(Boolean.valueOf(value)); + } + + value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION); + if (value != null) { + int level = UNKNOWN_TRANSACTIONISOLATION; + if ("NONE".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_NONE; + } else if ("READ_COMMITTED".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_READ_COMMITTED; + } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_READ_UNCOMMITTED; + } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_REPEATABLE_READ; + } else if ("SERIALIZABLE".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_SERIALIZABLE; + } else { + try { + level = Integer.parseInt(value); + } catch (NumberFormatException e) { + System.err.println("Could not parse defaultTransactionIsolation: " + value); + System.err.println("WARNING: defaultTransactionIsolation not set"); + System.err.println("using default value of database driver"); + level = UNKNOWN_TRANSACTIONISOLATION; + } + } + poolProperties.setDefaultTransactionIsolation(level); + } + + value = properties.getProperty(PROP_DEFAULTCATALOG); + if (value != null) { + poolProperties.setDefaultCatalog(value); + } + + value = properties.getProperty(PROP_DRIVERCLASSNAME); + if (value != null) { + poolProperties.setDriverClassName(value); + } + + value = properties.getProperty(PROP_MAXACTIVE); + if (value != null) { + poolProperties.setMaxActive(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MAXIDLE); + if (value != null) { + poolProperties.setMaxIdle(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MINIDLE); + if (value != null) { + poolProperties.setMinIdle(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_INITIALSIZE); + if (value != null) { + poolProperties.setInitialSize(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MAXWAIT); + if (value != null) { + poolProperties.setMaxWait(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_TESTONBORROW); + if (value != null) { + poolProperties.setTestOnBorrow(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_TESTONRETURN); + if (value != null) { + poolProperties.setTestOnReturn(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_TESTONCONNECT); + if (value != null) { + poolProperties.setTestOnConnect(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS); + if (value != null) { + poolProperties.setTimeBetweenEvictionRunsMillis(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN); + if (value != null) { + poolProperties.setNumTestsPerEvictionRun(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS); + if (value != null) { + poolProperties.setMinEvictableIdleTimeMillis(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_TESTWHILEIDLE); + if (value != null) { + poolProperties.setTestWhileIdle(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_PASSWORD); + if (value != null) { + poolProperties.setPassword(value); + } + + value = properties.getProperty(PROP_URL); + if (value != null) { + poolProperties.setUrl(value); + } + + value = properties.getProperty(PROP_USERNAME); + if (value != null) { + poolProperties.setUsername(value); + } + + value = properties.getProperty(PROP_VALIDATIONQUERY); + if (value != null) { + poolProperties.setValidationQuery(value); + } + + value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT); + if (value != null) { + poolProperties.setValidationQueryTimeout(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_VALIDATOR_CLASS_NAME); + if (value != null) { + poolProperties.setValidatorClassName(value); + } + + value = properties.getProperty(PROP_VALIDATIONINTERVAL); + if (value != null) { + poolProperties.setValidationInterval(Long.parseLong(value)); + } + + value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED); + if (value != null) { + poolProperties.setAccessToUnderlyingConnectionAllowed(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_REMOVEABANDONED); + if (value != null) { + poolProperties.setRemoveAbandoned(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT); + if (value != null) { + poolProperties.setRemoveAbandonedTimeout(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_LOGABANDONED); + if (value != null) { + poolProperties.setLogAbandoned(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS); + if (value != null) { + log.warn(PROP_POOLPREPAREDSTATEMENTS + " is not a valid setting, it will have no effect."); + } + + value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS); + if (value != null) { + log.warn(PROP_MAXOPENPREPAREDSTATEMENTS + " is not a valid setting, it will have no effect."); + } + + value = properties.getProperty(PROP_CONNECTIONPROPERTIES); + if (value != null) { + Properties p = getProperties(value); + poolProperties.setDbProperties(p); + } else { + poolProperties.setDbProperties(new Properties()); + } + + if (poolProperties.getUsername()!=null) { + poolProperties.getDbProperties().setProperty("user",poolProperties.getUsername()); + } + if (poolProperties.getPassword()!=null) { + poolProperties.getDbProperties().setProperty("password",poolProperties.getPassword()); + } + + value = properties.getProperty(PROP_INITSQL); + if (value != null) { + poolProperties.setInitSQL(value); + } + + value = properties.getProperty(PROP_INTERCEPTORS); + if (value != null) { + poolProperties.setJdbcInterceptors(value); + } + + value = properties.getProperty(PROP_JMX_ENABLED); + if (value != null) { + poolProperties.setJmxEnabled(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_FAIR_QUEUE); + if (value != null) { + poolProperties.setFairQueue(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_USE_EQUALS); + if (value != null) { + poolProperties.setUseEquals(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(OBJECT_NAME); + if (value != null) { + poolProperties.setName(ObjectName.quote(value)); + } + + value = properties.getProperty(PROP_ABANDONWHENPERCENTAGEFULL); + if (value != null) { + poolProperties.setAbandonWhenPercentageFull(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MAXAGE); + if (value != null) { + poolProperties.setMaxAge(Long.parseLong(value)); + } + + value = properties.getProperty(PROP_USE_CON_LOCK); + if (value != null) { + poolProperties.setUseLock(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_DATASOURCE); + if (value != null) { + //this should never happen + throw new IllegalArgumentException("Can't set dataSource property as a string, this must be a javax.sql.DataSource object."); + + } + + value = properties.getProperty(PROP_DATASOURCE_JNDI); + if (value != null) { + poolProperties.setDataSourceJNDI(value); + } + + value = properties.getProperty(PROP_SUSPECT_TIMEOUT); + if (value != null) { + poolProperties.setSuspectTimeout(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_ALTERNATE_USERNAME_ALLOWED); + if (value != null) { + poolProperties.setAlternateUsernameAllowed(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_COMMITONRETURN); + if (value != null) { + poolProperties.setCommitOnReturn(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_ROLLBACKONRETURN); + if (value != null) { + poolProperties.setRollbackOnReturn(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_USEDISPOSABLECONNECTIONFACADE); + if (value != null) { + poolProperties.setUseDisposableConnectionFacade(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_LOGVALIDATIONERRORS); + if (value != null) { + poolProperties.setLogValidationErrors(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_PROPAGATEINTERRUPTSTATE); + if (value != null) { + poolProperties.setPropagateInterruptState(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_IGNOREEXCEPTIONONPRELOAD); + if (value != null) { + poolProperties.setIgnoreExceptionOnPreLoad(Boolean.parseBoolean(value)); + } + + return poolProperties; + } + + /** + * Creates and configures a {@link DataSource} instance based on the + * given properties. + * + * @param properties the datasource configuration properties + * @return the datasource + * @throws Exception if an error occurs creating the data source + */ + public DataSource createDataSource(Properties properties) throws Exception { + return createDataSource(properties,null,false); + } + public DataSource createDataSource(Properties properties,Context context, boolean XA) throws Exception { + PoolConfiguration poolProperties = DataSourceFactory.parsePoolProperties(properties); + if (poolProperties.getDataSourceJNDI()!=null && poolProperties.getDataSource()==null) { + performJNDILookup(context, poolProperties); + } + org.apache.tomcat.jdbc.pool.DataSource dataSource = XA? + new org.apache.tomcat.jdbc.pool.XADataSource(poolProperties) : + new org.apache.tomcat.jdbc.pool.DataSource(poolProperties); + //initialise the pool itself + dataSource.createPool(); + // Return the configured DataSource instance + return dataSource; + } + + public void performJNDILookup(Context context, PoolConfiguration poolProperties) { + Object jndiDS = null; + try { + if (context!=null) { + jndiDS = context.lookup(poolProperties.getDataSourceJNDI()); + } else { + log.warn("dataSourceJNDI property is configured, but local JNDI context is null."); + } + } catch (NamingException e) { + log.debug("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the local context."); + } + if (jndiDS==null) { + try { + context = new InitialContext(); + jndiDS = context.lookup(poolProperties.getDataSourceJNDI()); + } catch (NamingException e) { + log.warn("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the InitialContext."); + } + } + if (jndiDS!=null) { + poolProperties.setDataSource(jndiDS); + } + } + + /** + * Parse properties from the string. Format of the string must be [propertyName=property;]*. + * @param propText The properties string + * @return the properties + */ + protected static Properties getProperties(String propText) { + return PoolProperties.getProperties(propText,null); + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java new file mode 100644 index 0000000..3e9b8ae --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java @@ -0,0 +1,1496 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.Iterator; +import java.util.Properties; +import java.util.concurrent.Future; +import java.util.logging.Logger; + +import javax.sql.XAConnection; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorDefinition; + +/** + * + * The DataSource proxy lets us implements methods that don't exist in the current + * compiler JDK but might be methods that are part of a future JDK DataSource interface. + *
+ * It's a trick to work around compiler issues when implementing interfaces. For example, + * I could put in Java 6 methods of javax.sql.DataSource here, and compile it with JDK 1.5 + * and still be able to run under Java 6 without getting NoSuchMethodException. + * + * @version 1.0 + */ + +public class DataSourceProxy implements PoolConfiguration { + private static final Log log = LogFactory.getLog(DataSourceProxy.class); + + protected volatile ConnectionPool pool = null; + + protected volatile PoolConfiguration poolProperties = null; + + public DataSourceProxy() { + this(new PoolProperties()); + } + + public DataSourceProxy(PoolConfiguration poolProperties) { + if (poolProperties == null) throw new NullPointerException("PoolConfiguration cannot be null."); + this.poolProperties = poolProperties; + } + + + @SuppressWarnings("unused") // Has to match signature in DataSource + public boolean isWrapperFor(Class iface) throws SQLException { + // we are not a wrapper of anything + return false; + } + + + @SuppressWarnings("unused") // Has to match signature in DataSource + public T unwrap(Class iface) throws SQLException { + //we can't unwrap anything + return null; + } + + /** + * Get a database connection. + * {@link javax.sql.DataSource#getConnection()} + * @param username The user name + * @param password The password + * @return the connection + * @throws SQLException Connection error + */ + public Connection getConnection(String username, String password) throws SQLException { + if (this.getPoolProperties().isAlternateUsernameAllowed()) { + if (pool == null) + return createPool().getConnection(username,password); + return pool.getConnection(username,password); + } else { + return getConnection(); + } + } + + public PoolConfiguration getPoolProperties() { + return poolProperties; + } + + /** + * Sets up the connection pool, by creating a pooling driver. + * @return the connection pool + * @throws SQLException Error creating pool + */ + public ConnectionPool createPool() throws SQLException { + if (pool != null) { + return pool; + } else { + return pCreatePool(); + } + } + + /** + * Sets up the connection pool, by creating a pooling driver. + */ + private synchronized ConnectionPool pCreatePool() throws SQLException { + if (pool != null) { + return pool; + } else { + pool = new ConnectionPool(poolProperties); + return pool; + } + } + + /** + * Get a database connection. + * {@link javax.sql.DataSource#getConnection()} + * @return the connection + * @throws SQLException Connection error + */ + public Connection getConnection() throws SQLException { + if (pool == null) + return createPool().getConnection(); + return pool.getConnection(); + } + + /** + * Invokes an sync operation to retrieve the connection. + * @return a Future containing a reference to the connection when it becomes available + * @throws SQLException Connection error + */ + public Future getConnectionAsync() throws SQLException { + if (pool == null) + return createPool().getConnectionAsync(); + return pool.getConnectionAsync(); + } + + /** + * Get a database connection. + * {@link javax.sql.XADataSource#getXAConnection()} + * @return the connection + * @throws SQLException Connection error + */ + public XAConnection getXAConnection() throws SQLException { + Connection con = getConnection(); + if (con instanceof XAConnection) { + return (XAConnection)con; + } else { + try { + con.close(); + } catch (Exception ignore) { + // Ignore + } + throw new SQLException("Connection from pool does not implement javax.sql.XAConnection"); + } + } + + /** + * Get a database connection. + * {@link javax.sql.XADataSource#getXAConnection(String, String)} + * @param username The user name + * @param password The password + * @return the connection + * @throws SQLException Connection error + */ + public XAConnection getXAConnection(String username, String password) throws SQLException { + Connection con = getConnection(username, password); + if (con instanceof XAConnection) { + return (XAConnection)con; + } else { + try { + con.close(); + } catch (Exception ignore) { + // Ignore + } + throw new SQLException("Connection from pool does not implement javax.sql.XAConnection"); + } + } + + + /** + * Get a database connection. + * {@link javax.sql.DataSource#getConnection()} + * @return the connection + * @throws SQLException Connection error + */ + public javax.sql.PooledConnection getPooledConnection() throws SQLException { + return (javax.sql.PooledConnection) getConnection(); + } + + /** + * Get a database connection. + * {@link javax.sql.DataSource#getConnection()} + * @param username unused + * @param password unused + * @return the connection + * @throws SQLException Connection error + */ + public javax.sql.PooledConnection getPooledConnection(String username, + String password) throws SQLException { + return (javax.sql.PooledConnection) getConnection(); + } + + public ConnectionPool getPool() { + try { + return createPool(); + }catch (SQLException x) { + log.error("Error during connection pool creation.", x); + return null; + } + } + + + public void close() { + close(false); + } + public void close(boolean all) { + try { + if (pool != null) { + final ConnectionPool p = pool; + pool = null; + if (p!=null) { + p.close(all); + } + } + }catch (Exception x) { + log.warn("Error during connection pool closure.", x); + } + } + + public int getPoolSize() { + final ConnectionPool p = pool; + if (p == null) return 0; + else return p.getSize(); + } + + + @Override + public String toString() { + return super.toString()+"{"+getPoolProperties()+"}"; + } + + +/*-----------------------------------------------------------------------*/ +// PROPERTIES WHEN NOT USED WITH FACTORY +/*------------------------------------------------------------------------*/ + + /** + * {@inheritDoc} + */ + + @Override + public String getPoolName() { + return pool.getName(); + } + + + public void setPoolProperties(PoolConfiguration poolProperties) { + this.poolProperties = poolProperties; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDriverClassName(String driverClassName) { + this.poolProperties.setDriverClassName(driverClassName); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setInitialSize(int initialSize) { + this.poolProperties.setInitialSize(initialSize); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setInitSQL(String initSQL) { + this.poolProperties.setInitSQL(initSQL); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setLogAbandoned(boolean logAbandoned) { + this.poolProperties.setLogAbandoned(logAbandoned); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMaxActive(int maxActive) { + this.poolProperties.setMaxActive(maxActive); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMaxIdle(int maxIdle) { + this.poolProperties.setMaxIdle(maxIdle); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMaxWait(int maxWait) { + this.poolProperties.setMaxWait(maxWait); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) { + this.poolProperties.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMinIdle(int minIdle) { + this.poolProperties.setMinIdle(minIdle); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { + this.poolProperties.setNumTestsPerEvictionRun(numTestsPerEvictionRun); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setPassword(String password) { + this.poolProperties.setPassword(password); + this.poolProperties.getDbProperties().setProperty("password",this.poolProperties.getPassword()); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setRemoveAbandoned(boolean removeAbandoned) { + this.poolProperties.setRemoveAbandoned(removeAbandoned); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) { + this.poolProperties.setRemoveAbandonedTimeout(removeAbandonedTimeout); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setTestOnBorrow(boolean testOnBorrow) { + this.poolProperties.setTestOnBorrow(testOnBorrow); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setTestOnConnect(boolean testOnConnect) { + this.poolProperties.setTestOnConnect(testOnConnect); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setTestOnReturn(boolean testOnReturn) { + this.poolProperties.setTestOnReturn(testOnReturn); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setTestWhileIdle(boolean testWhileIdle) { + this.poolProperties.setTestWhileIdle(testWhileIdle); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) { + this.poolProperties.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setUrl(String url) { + this.poolProperties.setUrl(url); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setUsername(String username) { + this.poolProperties.setUsername(username); + this.poolProperties.getDbProperties().setProperty("user",getPoolProperties().getUsername()); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setValidationInterval(long validationInterval) { + this.poolProperties.setValidationInterval(validationInterval); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setValidationQuery(String validationQuery) { + this.poolProperties.setValidationQuery(validationQuery); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setValidatorClassName(String className) { + this.poolProperties.setValidatorClassName(className); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setValidationQueryTimeout(int validationQueryTimeout) { + this.poolProperties.setValidationQueryTimeout(validationQueryTimeout); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setJdbcInterceptors(String interceptors) { + this.getPoolProperties().setJdbcInterceptors(interceptors); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setJmxEnabled(boolean enabled) { + this.getPoolProperties().setJmxEnabled(enabled); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setFairQueue(boolean fairQueue) { + this.getPoolProperties().setFairQueue(fairQueue); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setUseLock(boolean useLock) { + this.getPoolProperties().setUseLock(useLock); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDefaultCatalog(String catalog) { + this.getPoolProperties().setDefaultCatalog(catalog); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDefaultAutoCommit(Boolean autocommit) { + this.getPoolProperties().setDefaultAutoCommit(autocommit); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDefaultTransactionIsolation(int defaultTransactionIsolation) { + this.getPoolProperties().setDefaultTransactionIsolation(defaultTransactionIsolation); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setConnectionProperties(String properties) { + try { + java.util.Properties prop = DataSourceFactory + .getProperties(properties); + Iterator i = prop.keySet().iterator(); + while (i.hasNext()) { + String key = (String) i.next(); + String value = prop.getProperty(key); + getPoolProperties().getDbProperties().setProperty(key, value); + } + + } catch (Exception x) { + log.error("Unable to parse connection properties.", x); + throw new RuntimeException(x); + } + } + + /** + * {@inheritDoc} + */ + + @Override + public void setUseEquals(boolean useEquals) { + this.getPoolProperties().setUseEquals(useEquals); + } + + /** + * no-op + * {@link javax.sql.DataSource#getParentLogger} + * @return no return value + * @throws SQLFeatureNotSupportedException Unsupported + */ + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + throw new SQLFeatureNotSupportedException(); + } + + /** + * no-op + * {@link javax.sql.DataSource#getLogWriter} + * @return null + * @throws SQLException No exception + */ + public PrintWriter getLogWriter() throws SQLException { + return null; + } + + + /** + * no-op + * {@link javax.sql.DataSource#setLogWriter(PrintWriter)} + * @param out Ignored + * @throws SQLException No exception + */ + public void setLogWriter(PrintWriter out) throws SQLException { + // NOOP + } + + /** + * no-op + * {@link javax.sql.DataSource#getLoginTimeout} + * @return the timeout + */ + public int getLoginTimeout() { + if (poolProperties == null) { + return 0; + } else { + return poolProperties.getMaxWait() / 1000; + } + } + + /** + * {@link javax.sql.DataSource#setLoginTimeout(int)} + * @param i The timeout value + */ + public void setLoginTimeout(int i) { + if (poolProperties == null) { + return; + } else { + poolProperties.setMaxWait(1000 * i); + } + + } + + + /** + * {@inheritDoc} + */ + + @Override + public int getSuspectTimeout() { + return getPoolProperties().getSuspectTimeout(); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setSuspectTimeout(int seconds) { + getPoolProperties().setSuspectTimeout(seconds); + } + + //=============================================================================== +// Expose JMX attributes through Tomcat's dynamic reflection +//=============================================================================== + /** + * If the pool has not been created, it will be created during this call. + * @return the number of established but idle connections + */ + public int getIdle() { + try { + return createPool().getIdle(); + }catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * {@link #getIdle()} + * @return the number of established but idle connections + */ + public int getNumIdle() { + return getIdle(); + } + + /** + * Forces an abandon check on the connection pool. + * If connections that have been abandoned exists, they will be closed during this run + */ + public void checkAbandoned() { + try { + createPool().checkAbandoned(); + }catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * Forces a check for resizing of the idle connections + */ + public void checkIdle() { + try { + createPool().checkIdle(); + }catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * @return number of connections in use by the application + */ + public int getActive() { + try { + return createPool().getActive(); + }catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * @return number of connections in use by the application + * {@link DataSource#getActive()} + */ + public int getNumActive() { + return getActive(); + } + + /** + * @return number of threads waiting for a connection + */ + public int getWaitCount() { + try { + return createPool().getWaitCount(); + }catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * @return the current size of the pool + */ + public int getSize() { + try { + return createPool().getSize(); + }catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * Performs a validation on idle connections + */ + public void testIdle() { + try { + createPool().testAllIdle(); + }catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * The total number of connections borrowed from this pool. + * @return the borrowed connection count + */ + public long getBorrowedCount() { + try { + return createPool().getBorrowedCount(); + } catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * The total number of connections returned to this pool. + * @return the returned connection count + */ + public long getReturnedCount() { + try { + return createPool().getReturnedCount(); + } catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * The total number of connections created by this pool. + * @return the created connection count + */ + public long getCreatedCount() { + try { + return createPool().getCreatedCount(); + } catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * The total number of connections released from this pool. + * @return the released connection count + */ + public long getReleasedCount() { + try { + return createPool().getReleasedCount(); + } catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * The total number of connections reconnected by this pool. + * @return the reconnected connection count + */ + public long getReconnectedCount() { + try { + return createPool().getReconnectedCount(); + } catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * The total number of connections released by remove abandoned. + * @return the PoolCleaner removed abandoned connection count + */ + public long getRemoveAbandonedCount() { + try { + return createPool().getRemoveAbandonedCount(); + } catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * The total number of connections released by eviction. + * @return the PoolCleaner evicted idle connection count + */ + public long getReleasedIdleCount() { + try { + return createPool().getReleasedIdleCount(); + } catch (SQLException x) { + throw new RuntimeException(x); + } + } + + /** + * reset the statistics of this pool. + */ + public void resetStats() { + try { + createPool().resetStats(); + } catch (SQLException x) { + throw new RuntimeException(x); + } + } + + //========================================================= + // PROPERTIES / CONFIGURATION + //========================================================= + + /** + * {@inheritDoc} + */ + + @Override + public String getConnectionProperties() { + return getPoolProperties().getConnectionProperties(); + } + + /** + * {@inheritDoc} + */ + + @Override + public Properties getDbProperties() { + return getPoolProperties().getDbProperties(); + } + + /** + * {@inheritDoc} + */ + + @Override + public String getDefaultCatalog() { + return getPoolProperties().getDefaultCatalog(); + } + + /** + * {@inheritDoc} + */ + + @Override + public int getDefaultTransactionIsolation() { + return getPoolProperties().getDefaultTransactionIsolation(); + } + + /** + * {@inheritDoc} + */ + + @Override + public String getDriverClassName() { + return getPoolProperties().getDriverClassName(); + } + + + /** + * {@inheritDoc} + */ + + @Override + public int getInitialSize() { + return getPoolProperties().getInitialSize(); + } + + /** + * {@inheritDoc} + */ + + @Override + public String getInitSQL() { + return getPoolProperties().getInitSQL(); + } + + /** + * {@inheritDoc} + */ + + @Override + public String getJdbcInterceptors() { + return getPoolProperties().getJdbcInterceptors(); + } + + /** + * {@inheritDoc} + */ + + @Override + public int getMaxActive() { + return getPoolProperties().getMaxActive(); + } + + /** + * {@inheritDoc} + */ + + @Override + public int getMaxIdle() { + return getPoolProperties().getMaxIdle(); + } + + /** + * {@inheritDoc} + */ + + @Override + public int getMaxWait() { + return getPoolProperties().getMaxWait(); + } + + /** + * {@inheritDoc} + */ + + @Override + public int getMinEvictableIdleTimeMillis() { + return getPoolProperties().getMinEvictableIdleTimeMillis(); + } + + /** + * {@inheritDoc} + */ + + @Override + public int getMinIdle() { + return getPoolProperties().getMinIdle(); + } + + /** + * {@inheritDoc} + */ + + @Override + public long getMaxAge() { + return getPoolProperties().getMaxAge(); + } + + /** + * {@inheritDoc} + */ + + @Override + public String getName() { + return getPoolProperties().getName(); + } + + /** + * {@inheritDoc} + */ + + @Override + public int getNumTestsPerEvictionRun() { + return getPoolProperties().getNumTestsPerEvictionRun(); + } + + /** + * @return DOES NOT RETURN THE PASSWORD, IT WOULD SHOW UP IN JMX + */ + @Override + public String getPassword() { + return "Password not available as DataSource/JMX operation."; + } + + /** + * {@inheritDoc} + */ + + @Override + public int getRemoveAbandonedTimeout() { + return getPoolProperties().getRemoveAbandonedTimeout(); + } + + + /** + * {@inheritDoc} + */ + + @Override + public int getTimeBetweenEvictionRunsMillis() { + return getPoolProperties().getTimeBetweenEvictionRunsMillis(); + } + + /** + * {@inheritDoc} + */ + + @Override + public String getUrl() { + return getPoolProperties().getUrl(); + } + + /** + * {@inheritDoc} + */ + + @Override + public String getUsername() { + return getPoolProperties().getUsername(); + } + + /** + * {@inheritDoc} + */ + + @Override + public long getValidationInterval() { + return getPoolProperties().getValidationInterval(); + } + + /** + * {@inheritDoc} + */ + + @Override + public String getValidationQuery() { + return getPoolProperties().getValidationQuery(); + } + + /** + * {@inheritDoc} + */ + + @Override + public int getValidationQueryTimeout() { + return getPoolProperties().getValidationQueryTimeout(); + } + + /** + * {@inheritDoc} + */ + + @Override + public String getValidatorClassName() { + return getPoolProperties().getValidatorClassName(); + } + + /** + * {@inheritDoc} + */ + + @Override + public Validator getValidator() { + return getPoolProperties().getValidator(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setValidator(Validator validator) { + getPoolProperties().setValidator(validator); + } + + + /** + * {@inheritDoc} + */ + + @Override + public boolean isAccessToUnderlyingConnectionAllowed() { + return getPoolProperties().isAccessToUnderlyingConnectionAllowed(); + } + + /** + * {@inheritDoc} + */ + + @Override + public Boolean isDefaultAutoCommit() { + return getPoolProperties().isDefaultAutoCommit(); + } + + /** + * {@inheritDoc} + */ + + @Override + public Boolean isDefaultReadOnly() { + return getPoolProperties().isDefaultReadOnly(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isLogAbandoned() { + return getPoolProperties().isLogAbandoned(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isPoolSweeperEnabled() { + return getPoolProperties().isPoolSweeperEnabled(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isRemoveAbandoned() { + return getPoolProperties().isRemoveAbandoned(); + } + + /** + * {@inheritDoc} + */ + + @Override + public int getAbandonWhenPercentageFull() { + return getPoolProperties().getAbandonWhenPercentageFull(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isTestOnBorrow() { + return getPoolProperties().isTestOnBorrow(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isTestOnConnect() { + return getPoolProperties().isTestOnConnect(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isTestOnReturn() { + return getPoolProperties().isTestOnReturn(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isTestWhileIdle() { + return getPoolProperties().isTestWhileIdle(); + } + + + /** + * {@inheritDoc} + */ + + @Override + public Boolean getDefaultAutoCommit() { + return getPoolProperties().getDefaultAutoCommit(); + } + + /** + * {@inheritDoc} + */ + + @Override + public Boolean getDefaultReadOnly() { + return getPoolProperties().getDefaultReadOnly(); + } + + /** + * {@inheritDoc} + */ + + @Override + public InterceptorDefinition[] getJdbcInterceptorsAsArray() { + return getPoolProperties().getJdbcInterceptorsAsArray(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean getUseLock() { + return getPoolProperties().getUseLock(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isFairQueue() { + return getPoolProperties().isFairQueue(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isJmxEnabled() { + return getPoolProperties().isJmxEnabled(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isUseEquals() { + return getPoolProperties().isUseEquals(); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setAbandonWhenPercentageFull(int percentage) { + getPoolProperties().setAbandonWhenPercentageFull(percentage); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setAccessToUnderlyingConnectionAllowed(boolean accessToUnderlyingConnectionAllowed) { + getPoolProperties().setAccessToUnderlyingConnectionAllowed(accessToUnderlyingConnectionAllowed); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDbProperties(Properties dbProperties) { + getPoolProperties().setDbProperties(dbProperties); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDefaultReadOnly(Boolean defaultReadOnly) { + getPoolProperties().setDefaultReadOnly(defaultReadOnly); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMaxAge(long maxAge) { + getPoolProperties().setMaxAge(maxAge); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setName(String name) { + getPoolProperties().setName(name); + } + + /** + * {@inheritDoc} + */ + @Override + public void setDataSource(Object ds) { + getPoolProperties().setDataSource(ds); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getDataSource() { + return getPoolProperties().getDataSource(); + } + + + /** + * {@inheritDoc} + */ + @Override + public void setDataSourceJNDI(String jndiDS) { + getPoolProperties().setDataSourceJNDI(jndiDS); + } + + /** + * {@inheritDoc} + */ + @Override + public String getDataSourceJNDI() { + return getPoolProperties().getDataSourceJNDI(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAlternateUsernameAllowed() { + return getPoolProperties().isAlternateUsernameAllowed(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setAlternateUsernameAllowed(boolean alternateUsernameAllowed) { + getPoolProperties().setAlternateUsernameAllowed(alternateUsernameAllowed); + } + + /** + * {@inheritDoc} + */ + @Override + public void setCommitOnReturn(boolean commitOnReturn) { + getPoolProperties().setCommitOnReturn(commitOnReturn); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getCommitOnReturn() { + return getPoolProperties().getCommitOnReturn(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setRollbackOnReturn(boolean rollbackOnReturn) { + getPoolProperties().setRollbackOnReturn(rollbackOnReturn); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getRollbackOnReturn() { + return getPoolProperties().getRollbackOnReturn(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setUseDisposableConnectionFacade(boolean useDisposableConnectionFacade) { + getPoolProperties().setUseDisposableConnectionFacade(useDisposableConnectionFacade); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getUseDisposableConnectionFacade() { + return getPoolProperties().getUseDisposableConnectionFacade(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setLogValidationErrors(boolean logValidationErrors) { + getPoolProperties().setLogValidationErrors(logValidationErrors); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getLogValidationErrors() { + return getPoolProperties().getLogValidationErrors(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getPropagateInterruptState() { + return getPoolProperties().getPropagateInterruptState(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setPropagateInterruptState(boolean propagateInterruptState) { + getPoolProperties().setPropagateInterruptState(propagateInterruptState); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isIgnoreExceptionOnPreLoad() { + return getPoolProperties().isIgnoreExceptionOnPreLoad(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setIgnoreExceptionOnPreLoad(boolean ignoreExceptionOnPreLoad) { + getPoolProperties().setIgnoreExceptionOnPreLoad(ignoreExceptionOnPreLoad); + } + + public void purge() { + try { + createPool().purge(); + }catch (SQLException x) { + log.error("Unable to purge pool.",x); + } + } + + public void purgeOnReturn() { + try { + createPool().purgeOnReturn(); + }catch (SQLException x) { + log.error("Unable to purge pool.",x); + } + } +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java new file mode 100644 index 0000000..ed0e9db --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java @@ -0,0 +1,118 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.SQLException; + +/** + * A DisposableConnectionFacade object is the top most interceptor that wraps an + * object of type {@link PooledConnection}. The ProxyCutOffConnection intercepts + * two methods: + *
    + *
  • {@link java.sql.Connection#close()} - returns the connection to the + * pool then breaks the link between cutoff and the next interceptor. + * May be called multiple times.
  • + *
  • {@link java.lang.Object#toString()} - returns a custom string for this + * object
  • + *
+ * By default method comparisons is done on a String reference level, unless the + * {@link PoolConfiguration#setUseEquals(boolean)} has been called with a + * true argument. + */ +public class DisposableConnectionFacade extends JdbcInterceptor { + protected DisposableConnectionFacade(JdbcInterceptor interceptor) { + setUseEquals(interceptor.isUseEquals()); + setNext(interceptor); + } + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + } + + + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + public boolean equals(Object obj) { + return this==obj; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + if (compare(EQUALS_VAL, method)) { + return Boolean.valueOf( + this.equals(Proxy.getInvocationHandler(args[0]))); + } else if (compare(HASHCODE_VAL, method)) { + return Integer.valueOf(this.hashCode()); + } else if (getNext()==null) { + if (compare(ISCLOSED_VAL, method)) { + return Boolean.TRUE; + } + else if (compare(CLOSE_VAL, method)) { + return null; + } + else if (compare(ISVALID_VAL, method)) { + return Boolean.FALSE; + } + } + + try { + return super.invoke(proxy, method, args); + } catch (NullPointerException e) { + if (getNext() == null) { + if (compare(TOSTRING_VAL, method)) { + return "DisposableConnectionFacade[null]"; + } + throw new SQLException( + "PooledConnection has already been closed."); + } + + throw e; + } finally { + if (compare(CLOSE_VAL, method)) { + setNext(null); + } + } + } +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java new file mode 100644 index 0000000..c02bfa3 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java @@ -0,0 +1,579 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.NoSuchElementException; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; + +/** + * + * A simple implementation of a blocking queue with fairness waiting. + * invocations to method poll(...) will get handed out in the order they were received. + * Locking is fine grained, a shared lock is only used during the first level of contention, waiting is done in a + * lock per thread basis so that order is guaranteed once the thread goes into a suspended monitor state. + *
+ * Not all of the methods of the {@link java.util.concurrent.BlockingQueue} are implemented. + * + * @param Type of element in the queue + */ + +public class FairBlockingQueue implements BlockingQueue { + + /** + * This little sucker is used to reorder the way to do + * {@link java.util.concurrent.locks.Lock#lock()}, + * {@link java.util.concurrent.locks.Lock#unlock()} + * and + * {@link java.util.concurrent.CountDownLatch#countDown()} + * during the {@link #poll(long, TimeUnit)} operation. + * On Linux, it performs much better if we count down while we hold the global + * lock, on Solaris its the other way around. + * Until we have tested other platforms we only check for Linux. + */ + static final boolean isLinux = "Linux".equals(System.getProperty("os.name")) && + (!Boolean.getBoolean(FairBlockingQueue.class.getName()+".ignoreOS")); + + /** + * Phase one entry lock in order to give out + * per-thread-locks for the waiting phase we have + * a phase one lock during the contention period. + */ + final ReentrantLock lock = new ReentrantLock(false); + + /** + * All the objects in the pool are stored in a simple linked list + */ + final LinkedList items; + + /** + * All threads waiting for an object are stored in a linked list + */ + final LinkedList> waiters; + + /** + * Creates a new fair blocking queue. + */ + public FairBlockingQueue() { + items = new LinkedList<>(); + waiters = new LinkedList<>(); + } + + //------------------------------------------------------------------ + // USED BY CONPOOL IMPLEMENTATION + //------------------------------------------------------------------ + /** + * Will always return true, queue is unbounded. + * {@inheritDoc} + */ + @Override + public boolean offer(E e) { + //during the offer, we will grab the main lock + final ReentrantLock lock = this.lock; + lock.lock(); + ExchangeCountDownLatch c = null; + try { + //check to see if threads are waiting for an object + if (waiters.size() > 0) { + //if threads are waiting grab the latch for that thread + c = waiters.poll(); + //give the object to the thread instead of adding it to the pool + c.setItem(e); + if (isLinux) c.countDown(); + } else { + //we always add first, so that the most recently used object will be given out + items.addFirst(e); + } + } finally { + lock.unlock(); + } + //if we exchanged an object with another thread, wake it up. + if (!isLinux && c!=null) c.countDown(); + //we have an unbounded queue, so always return true + return true; + } + + /** + * Will never timeout, as it invokes the {@link #offer(Object)} method. + * Once a lock has been acquired, the + * {@inheritDoc} + */ + @Override + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { + return offer(e); + } + + /** + * Fair retrieval of an object in the queue. + * Objects are returned in the order the threads requested them. + * {@inheritDoc} + */ + @Override + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + E result = null; + final ReentrantLock lock = this.lock; + //acquire the global lock until we know what to do + lock.lock(); + try { + //check to see if we have objects + result = items.poll(); + if (result==null && timeout>0) { + //the queue is empty we will wait for an object + ExchangeCountDownLatch c = new ExchangeCountDownLatch<>(1); + //add to the bottom of the wait list + waiters.addLast(c); + //unlock the global lock + lock.unlock(); + boolean didtimeout = true; + InterruptedException interruptedException = null; + try { + //wait for the specified timeout + didtimeout = !c.await(timeout, unit); + } catch (InterruptedException ix) { + interruptedException = ix; + } + if (didtimeout) { + //if we timed out, or got interrupted + // remove ourselves from the waitlist + lock.lock(); + try { + waiters.remove(c); + } finally { + lock.unlock(); + } + } + //return the item we received, can be null if we timed out + result = c.getItem(); + if (null!=interruptedException) { + //we got interrupted + if ( null!=result) { + //we got a result - clear the interrupt status + //don't propagate cause we have removed a connection from pool + Thread.interrupted(); + } else { + throw interruptedException; + } + } + } else { + //we have an object, release + lock.unlock(); + } + } finally { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + return result; + } + + /** + * Request an item from the queue asynchronously + * @return - a future pending the result from the queue poll request + */ + public Future pollAsync() { + Future result = null; + final ReentrantLock lock = this.lock; + //grab the global lock + lock.lock(); + try { + //check to see if we have objects in the queue + E item = items.poll(); + if (item==null) { + //queue is empty, add ourselves as waiters + ExchangeCountDownLatch c = new ExchangeCountDownLatch<>(1); + waiters.addLast(c); + //return a future that will wait for the object + result = new ItemFuture<>(c); + } else { + //return a future with the item + result = new ItemFuture<>(item); + } + } finally { + lock.unlock(); + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean remove(Object e) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return items.remove(e); + } finally { + lock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + return items.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator iterator() { + return new FairIterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public E poll() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return items.poll(); + } finally { + lock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Object e) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return items.contains(e); + } finally { + lock.unlock(); + } + } + + + //------------------------------------------------------------------ + // NOT USED BY CONPOOL IMPLEMENTATION + //------------------------------------------------------------------ + /** + * {@inheritDoc} + */ + @Override + public boolean add(E e) { + return offer(e); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public int drainTo(Collection c, int maxElements) { + throw new UnsupportedOperationException("int drainTo(Collection c, int maxElements)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + + @Override + public int drainTo(Collection c) { + return drainTo(c,Integer.MAX_VALUE); + } + + /** + * {@inheritDoc} + */ + @Override + public void put(E e) throws InterruptedException { + offer(e); + } + + /** + * {@inheritDoc} + */ + @Override + public int remainingCapacity() { + return Integer.MAX_VALUE - size(); + } + + /** + * {@inheritDoc} + */ + @Override + public E take() throws InterruptedException { + return this.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean addAll(Collection c) { + Iterator i = c.iterator(); + while (i.hasNext()) { + E e = i.next(); + offer(e); + } + return true; + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public void clear() { + throw new UnsupportedOperationException("void clear()"); + + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean containsAll(Collection c) { + throw new UnsupportedOperationException("boolean containsAll(Collection c)"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEmpty() { + return size() == 0; + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException("boolean removeAll(Collection c)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException("boolean retainAll(Collection c)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public Object[] toArray() { + throw new UnsupportedOperationException("Object[] toArray()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public T[] toArray(T[] a) { + throw new UnsupportedOperationException(" T[] toArray(T[] a)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E element() { + throw new UnsupportedOperationException("E element()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E peek() { + throw new UnsupportedOperationException("E peek()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E remove() { + throw new UnsupportedOperationException("E remove()"); + } + + + + //------------------------------------------------------------------ + // Non cancellable Future used to check and see if a connection has been made available + //------------------------------------------------------------------ + protected class ItemFuture implements Future { + protected volatile T item = null; + protected volatile ExchangeCountDownLatch latch = null; + protected volatile boolean canceled = false; + + public ItemFuture(T item) { + this.item = item; + } + + public ItemFuture(ExchangeCountDownLatch latch) { + this.latch = latch; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; //don't allow cancel for now + } + + @Override + public T get() throws InterruptedException, ExecutionException { + if (item!=null) { + return item; + } else if (latch!=null) { + latch.await(); + return latch.getItem(); + } else { + throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception()); + } + } + + @Override + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + if (item!=null) { + return item; + } else if (latch!=null) { + boolean timedout = !latch.await(timeout, unit); + if (timedout) throw new TimeoutException(); + else return latch.getItem(); + } else { + throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception()); + } + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return (item!=null || latch.getItem()!=null); + } + + } + + //------------------------------------------------------------------ + // Count down latch that can be used to exchange information + //------------------------------------------------------------------ + protected class ExchangeCountDownLatch extends CountDownLatch { + protected volatile T item; + public ExchangeCountDownLatch(int i) { + super(i); + } + public T getItem() { + return item; + } + public void setItem(T item) { + this.item = item; + } + } + + //------------------------------------------------------------------ + // Iterator safe from concurrent modification exceptions + //------------------------------------------------------------------ + protected class FairIterator implements Iterator { + E[] elements = null; + int index; + E element = null; + + @SuppressWarnings("unchecked") // Can't create arrays of generic types + public FairIterator() { + final ReentrantLock lock = FairBlockingQueue.this.lock; + lock.lock(); + try { + elements = (E[]) new Object[FairBlockingQueue.this.items.size()]; + FairBlockingQueue.this.items.toArray(elements); + index = 0; + } finally { + lock.unlock(); + } + } + @Override + public boolean hasNext() { + return index + * Interceptors can receive a set of properties. Each sub class is responsible for parsing the properties during runtime when they + * are needed or simply override the {@link #setProperties(Map)} method. + * Properties arrive in a key-value pair of Strings as they were received through the configuration. + * This method is called once per cached connection object when the object is first configured. + * + * @version 1.0 + */ +public abstract class JdbcInterceptor implements InvocationHandler { + /** + * {@link java.sql.Connection#close()} method name + */ + public static final String CLOSE_VAL = "close"; + /** + * {@link Object#toString()} method name + */ + public static final String TOSTRING_VAL = "toString"; + /** + * {@link java.sql.Connection#isClosed()} method name + */ + public static final String ISCLOSED_VAL = "isClosed"; + /** + * {@link javax.sql.PooledConnection#getConnection()} method name + */ + public static final String GETCONNECTION_VAL = "getConnection"; + /** + * {@link java.sql.Wrapper#unwrap(Class)} method name + */ + public static final String UNWRAP_VAL = "unwrap"; + /** + * {@link java.sql.Wrapper#isWrapperFor(Class)} method name + */ + public static final String ISWRAPPERFOR_VAL = "isWrapperFor"; + + /** + * {@link java.sql.Connection#isValid(int)} method name + */ + public static final String ISVALID_VAL = "isValid"; + + /** + * {@link java.lang.Object#equals(Object)} + */ + public static final String EQUALS_VAL = "equals"; + + /** + * {@link java.lang.Object#hashCode()} + */ + public static final String HASHCODE_VAL = "hashCode"; + + /** + * Properties for this interceptor. + */ + protected Map properties = null; + + /** + * The next interceptor in the chain + */ + private volatile JdbcInterceptor next = null; + /** + * Property that decides how we do string comparison, default is to use + * {@link String#equals(Object)}. If set to false then the + * equality operator (==) is used. + */ + private boolean useEquals = true; + + /** + * Public constructor for instantiation through reflection + */ + public JdbcInterceptor() { + // NOOP + } + + /** + * Gets invoked each time an operation on {@link java.sql.Connection} is invoked. + * {@inheritDoc} + */ + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (getNext()!=null) return getNext().invoke(proxy,method,args); + else throw new NullPointerException(); + } + + /** + * Returns the next interceptor in the chain + * @return the next interceptor in the chain + */ + public JdbcInterceptor getNext() { + return next; + } + + /** + * configures the next interceptor in the chain + * @param next The next chain item + */ + public void setNext(JdbcInterceptor next) { + this.next = next; + } + + /** + * Performs a string comparison, using references unless the useEquals property is set to true. + * @param name1 The first name + * @param name2 The second name + * @return true if name1 is equal to name2 based on {@link #useEquals} + */ + public boolean compare(String name1, String name2) { + if (isUseEquals()) { + return name1.equals(name2); + } else { + return name1==name2; + } + } + + /** + * Compares a method name (String) to a method (Method) + * {@link #compare(String,String)} + * Uses reference comparison unless the useEquals property is set to true + * @param methodName The method name + * @param method The method + * @return true if the name matches + */ + public boolean compare(String methodName, Method method) { + return compare(methodName, method.getName()); + } + + /** + * Gets called each time the connection is borrowed from the pool + * This means that if an interceptor holds a reference to the connection + * the interceptor can be reused for another connection. + *
+ * This method may be called with null as both arguments when we are closing down the connection. + * @param parent - the connection pool owning the connection + * @param con - the pooled connection + */ + public abstract void reset(ConnectionPool parent, PooledConnection con); + + /** + * Called when {@link java.sql.Connection#close()} is called on the underlying connection. + * This is to notify the interceptors, that the physical connection has been released. + * Implementation of this method should be thought through with care, as no actions should trigger an exception. + * @param parent - the connection pool that this connection belongs to + * @param con - the pooled connection that holds this connection + * @param finalizing - if this connection is finalizing. True means that the pooled connection will not reconnect the underlying connection + */ + public void disconnected(ConnectionPool parent, PooledConnection con, boolean finalizing) { + } + + + /** + * Returns the properties configured for this interceptor + * @return the configured properties for this interceptor + */ + public Map getProperties() { + return properties; + } + + /** + * Called during the creation of an interceptor + * The properties can be set during the configuration of an interceptor + * Override this method to perform type casts between string values and object properties + * @param properties The properties + */ + public void setProperties(Map properties) { + this.properties = properties; + final String useEquals = "useEquals"; + InterceptorProperty p = properties.get(useEquals); + if (p!=null) { + setUseEquals(Boolean.parseBoolean(p.getValue())); + } + } + + /** + * @return true if the compare method uses the Object.equals(Object) method + * false if comparison is done on a reference level + */ + public boolean isUseEquals() { + return useEquals; + } + + /** + * Set to true if string comparisons (for the {@link #compare(String, Method)} and {@link #compare(String, String)} methods) should use the Object.equals(Object) method + * The default is false + * @param useEquals true if equals will be used for comparisons + */ + public void setUseEquals(boolean useEquals) { + this.useEquals = useEquals; + } + + /** + * This method is invoked by a connection pool when the pool is closed. + * Interceptor classes can override this method if they keep static + * variables or other tracking means around. + * This method is only invoked on a single instance of the interceptor, and not on every instance created. + * @param pool - the pool that is being closed. + */ + public void poolClosed(ConnectionPool pool) { + // NOOP + } + + /** + * This method is invoked by a connection pool when the pool is first started up, usually when the first connection is requested. + * Interceptor classes can override this method if they keep static + * variables or other tracking means around. + * This method is only invoked on a single instance of the interceptor, and not on every instance created. + * @param pool - the pool that is being closed. + */ + public void poolStarted(ConnectionPool pool) { + // NOOP + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/MultiLockFairBlockingQueue.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/MultiLockFairBlockingQueue.java new file mode 100644 index 0000000..a4b1f73 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/MultiLockFairBlockingQueue.java @@ -0,0 +1,584 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.NoSuchElementException; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; + +/** + * EXPERIMENTAL AND NOT YET COMPLETE! + * + * + * An implementation of a blocking queue with fairness waiting and lock dispersal to avoid contention. + * invocations to method poll(...) will get handed out in the order they were received. + * Locking is fine grained, a shared lock is only used during the first level of contention, waiting is done in a + * lock per thread basis so that order is guaranteed once the thread goes into a suspended monitor state. + *
+ * Not all of the methods of the {@link java.util.concurrent.BlockingQueue} are implemented. + * + * @param Type of element in the queue + */ + +public class MultiLockFairBlockingQueue implements BlockingQueue { + + final int LOCK_COUNT = Runtime.getRuntime().availableProcessors(); + + final AtomicInteger putQueue = new AtomicInteger(0); + final AtomicInteger pollQueue = new AtomicInteger(0); + + public int getNextPut() { + int idx = Math.abs(putQueue.incrementAndGet()) % LOCK_COUNT; + return idx; + } + + public int getNextPoll() { + int idx = Math.abs(pollQueue.incrementAndGet()) % LOCK_COUNT; + return idx; + } + /** + * Phase one entry lock in order to give out + * per-thread-locks for the waiting phase we have + * a phase one lock during the contention period. + */ + private final ReentrantLock[] locks = new ReentrantLock[LOCK_COUNT]; + + /** + * All the objects in the pool are stored in a simple linked list + */ + final LinkedList[] items; + + /** + * All threads waiting for an object are stored in a linked list + */ + final LinkedList>[] waiters; + + /** + * Creates a new fair blocking queue. + */ + @SuppressWarnings("unchecked") // Can create arrays of generic types + public MultiLockFairBlockingQueue() { + items = new LinkedList[LOCK_COUNT]; + waiters = new LinkedList[LOCK_COUNT]; + for (int i=0; i(); + waiters[i] = new LinkedList<>(); + locks[i] = new ReentrantLock(false); + } + } + + //------------------------------------------------------------------ + // USED BY CONPOOL IMPLEMENTATION + //------------------------------------------------------------------ + /** + * Will always return true, queue is unbounded. + * {@inheritDoc} + */ + @Override + public boolean offer(E e) { + int idx = getNextPut(); + //during the offer, we will grab the main lock + final ReentrantLock lock = this.locks[idx]; + lock.lock(); + ExchangeCountDownLatch c = null; + try { + //check to see if threads are waiting for an object + if (waiters[idx].size() > 0) { + //if threads are waiting grab the latch for that thread + c = waiters[idx].poll(); + //give the object to the thread instead of adding it to the pool + c.setItem(e); + } else { + //we always add first, so that the most recently used object will be given out + items[idx].addFirst(e); + } + } finally { + lock.unlock(); + } + //if we exchanged an object with another thread, wake it up. + if (c!=null) c.countDown(); + //we have an unbounded queue, so always return true + return true; + } + + /** + * Will never timeout, as it invokes the {@link #offer(Object)} method. + * Once a lock has been acquired, the + * {@inheritDoc} + */ + @Override + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { + return offer(e); + } + + /** + * Fair retrieval of an object in the queue. + * Objects are returned in the order the threads requested them. + * {@inheritDoc} + */ + @Override + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + int idx = getNextPoll(); + E result = null; + final ReentrantLock lock = this.locks[idx]; + try { + //acquire the global lock until we know what to do + lock.lock(); + //check to see if we have objects + result = items[idx].poll(); + if (result==null && timeout>0) { + //the queue is empty we will wait for an object + ExchangeCountDownLatch c = new ExchangeCountDownLatch<>(1); + //add to the bottom of the wait list + waiters[idx].addLast(c); + //unlock the global lock + lock.unlock(); + //wait for the specified timeout + if (!c.await(timeout, unit)) { + //if we timed out, remove ourselves from the waitlist + lock.lock(); + waiters[idx].remove(c); + lock.unlock(); + } + //return the item we received, can be null if we timed out + result = c.getItem(); + } else { + //we have an object, release + lock.unlock(); + } + } finally { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + return result; + } + + /** + * Request an item from the queue asynchronously + * @return - a future pending the result from the queue poll request + */ + public Future pollAsync() { + int idx = getNextPoll(); + Future result = null; + final ReentrantLock lock = this.locks[idx]; + try { + //grab the global lock + lock.lock(); + //check to see if we have objects in the queue + E item = items[idx].poll(); + if (item==null) { + //queue is empty, add ourselves as waiters + ExchangeCountDownLatch c = new ExchangeCountDownLatch<>(1); + waiters[idx].addLast(c); + //return a future that will wait for the object + result = new ItemFuture<>(c); + } else { + //return a future with the item + result = new ItemFuture<>(item); + } + } finally { + lock.unlock(); + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean remove(Object e) { + for (int idx=0; idx iterator() { + return new FairIterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public E poll() { + int idx = getNextPoll(); + final ReentrantLock lock = this.locks[idx]; + lock.lock(); + try { + return items[idx].poll(); + } finally { + lock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Object e) { + for (int idx=0; idx c, int maxElements) { + throw new UnsupportedOperationException("int drainTo(Collection c, int maxElements)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public int drainTo(Collection c) { + return drainTo(c,Integer.MAX_VALUE); + } + + /** + * {@inheritDoc} + */ + @Override + public void put(E e) throws InterruptedException { + offer(e); + } + + /** + * {@inheritDoc} + */ + @Override + public int remainingCapacity() { + return Integer.MAX_VALUE - size(); + } + + /** + * {@inheritDoc} + */ + @Override + public E take() throws InterruptedException { + return this.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean addAll(Collection c) { + Iterator i = c.iterator(); + while (i.hasNext()) { + E e = i.next(); + offer(e); + } + return true; + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public void clear() { + throw new UnsupportedOperationException("void clear()"); + + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean containsAll(Collection c) { + throw new UnsupportedOperationException("boolean containsAll(Collection c)"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEmpty() { + return size() == 0; + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException("boolean removeAll(Collection c)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException("boolean retainAll(Collection c)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public Object[] toArray() { + throw new UnsupportedOperationException("Object[] toArray()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public T[] toArray(T[] a) { + throw new UnsupportedOperationException(" T[] toArray(T[] a)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E element() { + throw new UnsupportedOperationException("E element()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E peek() { + throw new UnsupportedOperationException("E peek()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E remove() { + throw new UnsupportedOperationException("E remove()"); + } + + + + //------------------------------------------------------------------ + // Non cancellable Future used to check and see if a connection has been made available + //------------------------------------------------------------------ + protected class ItemFuture implements Future { + protected volatile T item = null; + protected volatile ExchangeCountDownLatch latch = null; + protected volatile boolean canceled = false; + + public ItemFuture(T item) { + this.item = item; + } + + public ItemFuture(ExchangeCountDownLatch latch) { + this.latch = latch; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; //don't allow cancel for now + } + + @Override + public T get() throws InterruptedException, ExecutionException { + if (item!=null) { + return item; + } else if (latch!=null) { + latch.await(); + return latch.getItem(); + } else { + throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception()); + } + } + + @Override + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + if (item!=null) { + return item; + } else if (latch!=null) { + boolean timedout = !latch.await(timeout, unit); + if (timedout) throw new TimeoutException(); + else return latch.getItem(); + } else { + throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception()); + } + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return (item!=null || latch.getItem()!=null); + } + + } + + //------------------------------------------------------------------ + // Count down latch that can be used to exchange information + //------------------------------------------------------------------ + protected class ExchangeCountDownLatch extends CountDownLatch { + protected volatile T item; + public ExchangeCountDownLatch(int i) { + super(i); + } + public T getItem() { + return item; + } + public void setItem(T item) { + this.item = item; + } + } + + //------------------------------------------------------------------ + // Iterator safe from concurrent modification exceptions + //------------------------------------------------------------------ + protected class FairIterator implements Iterator { + E[] elements = null; + int index; + E element = null; + + @SuppressWarnings("unchecked") // Can't create arrays of generic types + public FairIterator() { + ArrayList list = new ArrayList<>(MultiLockFairBlockingQueue.this.size()); + for (int idx=0; idxtrue if a fair queue is being used by the connection pool + * @return true if a fair waiting queue is being used + */ + public boolean isFairQueue(); + + /** + * Set to true if you wish that calls to getConnection + * should be treated fairly in a true FIFO fashion. + * This uses the {@link FairBlockingQueue} implementation for the list of the idle connections. + * The default value is true. + * This flag is required when you want to use asynchronous connection retrieval. + * @param fairQueue true to use a fair queue + */ + public void setFairQueue(boolean fairQueue); + + /** + * Property not used. Access is always allowed. + * Access can be achieved by calling unwrap on the pooled connection. see {@link javax.sql.DataSource} interface + * or call getConnection through reflection or cast the object as {@link javax.sql.PooledConnection} + * @return true + */ + public boolean isAccessToUnderlyingConnectionAllowed(); + + /** + * No-op + * @param accessToUnderlyingConnectionAllowed parameter ignored + */ + public void setAccessToUnderlyingConnectionAllowed(boolean accessToUnderlyingConnectionAllowed); + + /** + * The connection properties that will be sent to the JDBC driver when establishing new connections. + * Format of the string is [propertyName=property;]
+ * NOTE - The "user" and "password" properties will be passed explicitly, so they do not need to be included here. + * The default value is null. + * @return the connection properties + */ + public String getConnectionProperties(); + + /** + * The properties that will be passed into {@link java.sql.Driver#connect(String, Properties)} method. + * Username and password do not need to be stored here, they will be passed into the properties right before the connection is established. + * @param connectionProperties properties - Format of the string is [propertyName=property;]* + * Example: prop1=value1;prop2=value2 + */ + public void setConnectionProperties(String connectionProperties); + + /** + * Returns the database properties that are passed into the {@link java.sql.Driver#connect(String, Properties)} method. + * @return database properties that are passed into the {@link java.sql.Driver#connect(String, Properties)} method. + */ + public Properties getDbProperties(); + + /** + * Overrides the database properties passed into the {@link java.sql.Driver#connect(String, Properties)} method. + * @param dbProperties The database properties + */ + public void setDbProperties(Properties dbProperties); + + /** + * The default auto-commit state of connections created by this pool. + * If not set (null), default is JDBC driver default (If set to null then the {@link java.sql.Connection#setAutoCommit(boolean)} method will not be called.) + * @return the default auto commit setting, null is Driver default. + */ + public Boolean isDefaultAutoCommit(); + + /** + * The default auto-commit state of connections created by this pool. + * If not set (null), default is JDBC driver default (If set to null then the {@link java.sql.Connection#setAutoCommit(boolean)} method will not be called.) + * @return the default auto commit setting, null is Driver default. + */ + public Boolean getDefaultAutoCommit(); + + /** + * The default auto-commit state of connections created by this pool. + * If not set (null), default is JDBC driver default (If set to null then the {@link java.sql.Connection#setAutoCommit(boolean)} method will not be called.) + * @param defaultAutoCommit default auto commit setting, null is Driver default. + */ + public void setDefaultAutoCommit(Boolean defaultAutoCommit); + + /** + * If non null, during connection creation the method {@link java.sql.Connection#setCatalog(String)} will be called with the set value. + * @return the default catalog, null if not set and accepting the driver default. + */ + public String getDefaultCatalog(); + + /** + * If non null, during connection creation the method {@link java.sql.Connection#setCatalog(String)} will be called with the set value. + * @param defaultCatalog null if not set and accepting the driver default. + */ + public void setDefaultCatalog(String defaultCatalog); + + /** + * If non null, during connection creation the method {@link java.sql.Connection#setReadOnly(boolean)} will be called with the set value. + * @return null if not set and accepting the driver default otherwise the read only value + */ + public Boolean isDefaultReadOnly(); + + /** + * If non null, during connection creation the method {@link java.sql.Connection#setReadOnly(boolean)} will be called with the set value. + * @return null if not set and accepting the driver default otherwise the read only value + */ + public Boolean getDefaultReadOnly(); + + /** + * If non null, during connection creation the method {@link java.sql.Connection#setReadOnly(boolean)} will be called with the set value. + * @param defaultReadOnly null if not set and accepting the driver default. + */ + public void setDefaultReadOnly(Boolean defaultReadOnly); + + + /** + * Returns the default transaction isolation level. If set to {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION} the method + * {@link java.sql.Connection#setTransactionIsolation(int)} will not be called during connection creation. + * @return driver transaction isolation level, or -1 {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION} if not set. + */ + public int getDefaultTransactionIsolation(); + + /** + * If set to {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION} the method + * {@link java.sql.Connection#setTransactionIsolation(int)} will not be called during connection creation. Otherwise the method + * will be called with the isolation level set by this property. + * @param defaultTransactionIsolation a value of {@link java.sql.Connection#TRANSACTION_NONE}, {@link java.sql.Connection#TRANSACTION_READ_COMMITTED}, + * {@link java.sql.Connection#TRANSACTION_READ_UNCOMMITTED}, {@link java.sql.Connection#TRANSACTION_REPEATABLE_READ}, + * {@link java.sql.Connection#TRANSACTION_SERIALIZABLE} or {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION} + * The last value will not be set on the connection. + */ + public void setDefaultTransactionIsolation(int defaultTransactionIsolation); + + /** + * The fully qualified Java class name of the JDBC driver to be used. The driver has to be accessible from the same classloader as tomcat-jdbc.jar + * @return fully qualified JDBC driver name. + */ + public String getDriverClassName(); + + /** + * The fully qualified Java class name of the JDBC driver to be used. The driver has to be accessible from the same classloader as tomcat-jdbc.jar + * @param driverClassName a fully qualified Java class name of a {@link java.sql.Driver} implementation. + */ + public void setDriverClassName(String driverClassName); + + /** + * Returns the number of connections that will be established when the connection pool is started. + * Default value is 10 + * @return number of connections to be started when pool is started + */ + public int getInitialSize(); + + /** + * Set the number of connections that will be established when the connection pool is started. + * Default value is 10. + * If this value exceeds {@link #setMaxActive(int)} it will automatically be lowered. + * @param initialSize the number of connections to be established. + * + */ + public void setInitialSize(int initialSize); + + /** + * boolean flag to set if stack traces should be logged for application code which abandoned a Connection. + * Logging of abandoned Connections adds overhead for every Connection borrow because a stack trace has to be generated. + * The default value is false. + * @return true if the connection pool logs stack traces when connections are borrowed from the pool. + */ + public boolean isLogAbandoned(); + + /** + * boolean flag to set if stack traces should be logged for application code which abandoned a Connection. + * Logging of abandoned Connections adds overhead for every Connection borrow because a stack trace has to be generated. + * The default value is false. + * @param logAbandoned set to true if stack traces should be recorded when {@link DataSource#getConnection()} is called. + */ + public void setLogAbandoned(boolean logAbandoned); + + /** + * The maximum number of active connections that can be allocated from this pool at the same time. The default value is 100 + * @return the maximum number of connections used by this pool + */ + public int getMaxActive(); + + /** + * The maximum number of active connections that can be allocated from this pool at the same time. The default value is 100 + * @param maxActive hard limit for number of managed connections by this pool + */ + public void setMaxActive(int maxActive); + + + /** + * The maximum number of connections that should be kept in the idle pool if {@link #isPoolSweeperEnabled()} returns false. + * If the If {@link #isPoolSweeperEnabled()} returns true, then the idle pool can grow up to {@link #getMaxActive} + * and will be shrunk according to {@link #getMinEvictableIdleTimeMillis()} setting. + * Default value is maxActive:100 + * @return the maximum number of idle connections. + */ + public int getMaxIdle(); + + /** + * The maximum number of connections that should be kept in the idle pool if {@link #isPoolSweeperEnabled()} returns false. + * If the If {@link #isPoolSweeperEnabled()} returns true, then the idle pool can grow up to {@link #getMaxActive} + * and will be shrunk according to {@link #getMinEvictableIdleTimeMillis()} setting. + * Default value is maxActive:100 + * @param maxIdle the maximum size of the idle pool + */ + public void setMaxIdle(int maxIdle); + + /** + * The maximum number of milliseconds that the pool will wait (when there are no available connections and the + * {@link #getMaxActive} has been reached) for a connection to be returned + * before throwing an exception. Default value is 30000 (30 seconds) + * @return the number of milliseconds to wait for a connection to become available if the pool is maxed out. + */ + public int getMaxWait(); + + /** + * The maximum number of milliseconds that the pool will wait (when there are no available connections and the + * {@link #getMaxActive} has been reached) for a connection to be returned + * before throwing an exception. Default value is 30000 (30 seconds) + * @param maxWait the maximum number of milliseconds to wait. + */ + public void setMaxWait(int maxWait); + + /** + * The minimum amount of time an object must sit idle in the pool before it is eligible for eviction. + * The default value is 60000 (60 seconds). + * @return the minimum amount of idle time in milliseconds before a connection is considered idle and eligible for eviction. + */ + public int getMinEvictableIdleTimeMillis(); + + /** + * The minimum amount of time an object must sit idle in the pool before it is eligible for eviction. + * The default value is 60000 (60 seconds). + * @param minEvictableIdleTimeMillis the number of milliseconds a connection must be idle to be eligible for eviction. + */ + public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis); + + /** + * The minimum number of established connections that should be kept in the pool at all times. + * The connection pool can shrink below this number if validation queries fail and connections get closed. + * Default value is derived from {@link #getInitialSize()} (also see {@link #setTestWhileIdle(boolean)} + * The idle pool will not shrink below this value during an eviction run, hence the number of actual connections + * can be between {@link #getMinIdle()} and somewhere between {@link #getMaxIdle()} and {@link #getMaxActive()} + * @return the minimum number of idle or established connections + */ + public int getMinIdle(); + + /** + * The minimum number of established connections that should be kept in the pool at all times. + * The connection pool can shrink below this number if validation queries fail and connections get closed. + * Default value is derived from {@link #getInitialSize()} (also see {@link #setTestWhileIdle(boolean)} + * The idle pool will not shrink below this value during an eviction run, hence the number of actual connections + * can be between {@link #getMinIdle()} and somewhere between {@link #getMaxIdle()} and {@link #getMaxActive()} + * + * @param minIdle the minimum number of idle or established connections + */ + public void setMinIdle(int minIdle); + + /** + * Returns the name of the connection pool. By default a JVM unique random name is assigned. + * @return the name of the pool, should be unique in a JVM + */ + public String getName(); + + /** + * Sets the name of the connection pool + * @param name the name of the pool, should be unique in a runtime JVM + */ + public void setName(String name); + + /** + * Property not used + * @return unknown value + */ + public int getNumTestsPerEvictionRun(); + + /** + * Property not used + * @param numTestsPerEvictionRun parameter ignored. + */ + public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun); + + /** + * Returns the password used when establishing connections to the database. + * @return the password in string format + */ + public String getPassword(); + + /** + * Sets the password to establish the connection with. + * The password will be included as a database property with the name 'password'. + * @param password The password + * @see #getDbProperties() + */ + public void setPassword(String password); + + /** + * @see #getName() + * @return the pool name + */ + public String getPoolName(); + + /** + * Returns the username used to establish the connection with + * @return the username used to establish the connection with + */ + public String getUsername(); + + /** + * Sets the username used to establish the connection with + * It will also be a property called 'user' in the database properties. + * @param username The user name + * @see #getDbProperties() + */ + public void setUsername(String username); + + + /** + * boolean flag to remove abandoned connections if they exceed the removeAbandonedTimout. + * If set to true a connection is considered abandoned and eligible for removal if it has + * been in use longer than the {@link #getRemoveAbandonedTimeout()} and the condition for + * {@link #getAbandonWhenPercentageFull()} is met. + * Setting this to true can recover db connections from applications that fail to close a connection. + * See also {@link #isLogAbandoned()} The default value is false. + * @return true if abandoned connections can be closed and expelled out of the pool + */ + public boolean isRemoveAbandoned(); + + /** + * boolean flag to remove abandoned connections if they exceed the removeAbandonedTimout. + * If set to true a connection is considered abandoned and eligible for removal if it has + * been in use longer than the {@link #getRemoveAbandonedTimeout()} and the condition for + * {@link #getAbandonWhenPercentageFull()} is met. + * Setting this to true can recover db connections from applications that fail to close a connection. + * See also {@link #isLogAbandoned()} The default value is false. + * @param removeAbandoned set to true if abandoned connections can be closed and expelled out of the pool + */ + public void setRemoveAbandoned(boolean removeAbandoned); + + /** + * The time in seconds before a connection can be considered abandoned. + * The timer can be reset upon queries using an interceptor. + * @param removeAbandonedTimeout the time in seconds before a used connection can be considered abandoned + * @see org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer + */ + public void setRemoveAbandonedTimeout(int removeAbandonedTimeout); + + /** + * The time in seconds before a connection can be considered abandoned. + * The timer can be reset upon queries using an interceptor. + * @see org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer + * @return the time in seconds before a used connection can be considered abandoned + */ + public int getRemoveAbandonedTimeout(); + + /** + * The indication of whether objects will be validated before being borrowed from the pool. + * If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another. + * NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string. + * Default value is false + * In order to have a more efficient validation, see {@link #setValidationInterval(long)} + * @return true if the connection is to be validated upon borrowing a connection from the pool + * @see #getValidationInterval() + */ + public boolean isTestOnBorrow(); + + /** + * The indication of whether objects will be validated before being borrowed from the pool. + * If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another. + * NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string. + * Default value is false + * In order to have a more efficient validation, see {@link #setValidationInterval(long)} + * @param testOnBorrow set to true if validation should take place before a connection is handed out to the application + * @see #getValidationInterval() + */ + public void setTestOnBorrow(boolean testOnBorrow); + + /** + * The indication of whether objects will be validated after being returned to the pool. + * If the object fails to validate, it will be dropped from the pool. + * NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string. + * Default value is false + * In order to have a more efficient validation, see {@link #setValidationInterval(long)} + * @return true if validation should take place after a connection is returned to the pool + * @see #getValidationInterval() + */ + public boolean isTestOnReturn(); + + /** + * The indication of whether objects will be validated after being returned to the pool. + * If the object fails to validate, it will be dropped from the pool. + * NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string. + * Default value is false + * In order to have a more efficient validation, see {@link #setValidationInterval(long)} + * @param testOnReturn true if validation should take place after a connection is returned to the pool + * @see #getValidationInterval() + */ + public void setTestOnReturn(boolean testOnReturn); + + + /** + * Set to true if query validation should take place while the connection is idle. + * @return true if validation should take place during idle checks + * @see #setTimeBetweenEvictionRunsMillis(int) + */ + public boolean isTestWhileIdle(); + + /** + * Set to true if query validation should take place while the connection is idle. + * @param testWhileIdle true if validation should take place during idle checks + * @see #setTimeBetweenEvictionRunsMillis(int) + */ + public void setTestWhileIdle(boolean testWhileIdle); + + /** + * The number of milliseconds to sleep between runs of the idle connection validation, abandoned cleaner + * and idle pool resizing. This value should not be set under 1 second. + * It dictates how often we check for idle, abandoned connections, and how often we validate idle connection and resize the idle pool. + * The default value is 5000 (5 seconds) + * @return the sleep time in between validations in milliseconds + */ + public int getTimeBetweenEvictionRunsMillis(); + + /** + * The number of milliseconds to sleep between runs of the idle connection validation, abandoned cleaner + * and idle pool resizing. This value should not be set under 1 second. + * It dictates how often we check for idle, abandoned connections, and how often we validate idle connection and resize the idle pool. + * The default value is 5000 (5 seconds) + * @param timeBetweenEvictionRunsMillis the sleep time in between validations in milliseconds + */ + public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis); + + /** + * The URL used to connect to the database + * @return the configured URL for this connection pool + * @see java.sql.Driver#connect(String, Properties) + */ + public String getUrl(); + + /** + * Sets the URL used to connect to the database + * @param url the configured URL for this connection pool + * @see java.sql.Driver#connect(String, Properties) + */ + public void setUrl(String url); + + /** + * The SQL query that will be used to validate connections from this + * pool before returning them to the caller or pool. + * If specified, this query does not have to return any data, + * it just can't throw a SQLException. + * The default value is null. + * Example values are SELECT 1(mysql), + * select 1 from dual(oracle), + * SELECT 1(MS Sql Server) + * @return the query used for validation or null if no validation is performed + */ + public String getValidationQuery(); + + /** + * The SQL query that will be used to validate connections from this + * pool before returning them to the caller or pool. + * If specified, this query does not have to return any data, + * it just can't throw a SQLException. + * The default value is null. + * Example values are SELECT 1(mysql), + * select 1 from dual(oracle), + * SELECT 1(MS Sql Server) + * @param validationQuery the query used for validation or null if no validation is performed + */ + public void setValidationQuery(String validationQuery); + + /** + * The timeout in seconds before a connection validation queries fail. + * A value less than or equal to zero will disable this feature. Defaults to -1. + * @return the timeout value in seconds + */ + public int getValidationQueryTimeout(); + + /** + * The timeout in seconds before a connection validation queries fail. + * A value less than or equal to zero will disable this feature. Defaults to -1. + * @param validationQueryTimeout The timeout value + */ + public void setValidationQueryTimeout(int validationQueryTimeout); + + /** + * Return the name of the optional validator class - may be null. + * + * @return the name of the optional validator class - may be null + */ + public String getValidatorClassName(); + + /** + * Set the name for an optional validator class which will be used in place of test queries. If set to + * null, standard validation will be used. + * + * @param className the name of the optional validator class + */ + public void setValidatorClassName(String className); + + /** + * @return the optional validator object - may be null + */ + public Validator getValidator(); + + /** + * Sets the validator object + * If this is a non null object, it will be used as a validator instead of the validationQuery + * If this is null, remove the usage of the validator. + * @param validator The validator object + */ + public void setValidator(Validator validator); + + /** + * avoid excess validation, only run validation at most at this frequency - time in milliseconds. + * If a connection is due for validation, but has been validated previously + * within this interval, it will not be validated again. + * The default value is 3000 (3 seconds). + * @return the validation interval in milliseconds + */ + public long getValidationInterval(); + + /** + * avoid excess validation, only run validation at most at this frequency - time in milliseconds. + * If a connection is due for validation, but has been validated previously + * within this interval, it will not be validated again. + * The default value is 3000 (3 seconds). + * @param validationInterval the validation interval in milliseconds + */ + public void setValidationInterval(long validationInterval); + + /** + * A custom query to be run when a connection is first created. The default value is null. + * This query only runs once per connection, and that is when a new connection is established to the database. + * If this value is non null, it will replace the validation query during connection creation. + * @return the init SQL used to run against the DB or null if not set + */ + public String getInitSQL(); + + /** + * A custom query to be run when a connection is first created. The default value is null. + * This query only runs once per connection, and that is when a new connection is established to the database. + * If this value is non null, it will replace the validation query during connection creation. + * @param initSQL the init SQL used to run against the DB or null if no query should be executed + */ + public void setInitSQL(String initSQL); + + /** + * Returns true if we should run the validation query when connecting to the database for the first time on a connection. + * Normally this is always set to false, unless one wants to use the validationQuery as an init query. + * @return true if we should run the validation query upon connect + */ + public boolean isTestOnConnect(); + + /** + * Set to true if we should run the validation query when connecting to the database for the first time on a connection. + * Normally this is always set to false, unless one wants to use the validationQuery as an init query. + * Setting an {@link #setInitSQL(String)} will override this setting, as the init SQL will be used instead of the validation query + * @param testOnConnect set to true if we should run the validation query upon connect + */ + public void setTestOnConnect(boolean testOnConnect); + + /** + * A semicolon separated list of classnames extending {@link org.apache.tomcat.jdbc.pool.JdbcInterceptor} class. + * These interceptors will be inserted as an interceptor into the chain of operations on a java.sql.Connection object. + * Example interceptors are {@link org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer StatementFinalizer} to close all + * used statements during the session. + * {@link org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer ResetAbandonedTimer} resets the timer upon every operation + * on the connection or a statement. + * {@link org.apache.tomcat.jdbc.pool.interceptor.ConnectionState ConnectionState} caches the auto commit, read only and catalog settings to avoid round trips to the DB. + * The default value is null. + * @return the interceptors that are used for connections. + * Example format: 'ConnectionState(useEquals=true,fast=yes);ResetAbandonedTimer' + */ + public String getJdbcInterceptors(); + + /** + * A semicolon separated list of classnames extending {@link org.apache.tomcat.jdbc.pool.JdbcInterceptor} class. + * These interceptors will be inserted as an interceptor into the chain of operations on a java.sql.Connection object. + * Example interceptors are {@link org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer StatementFinalizer} to close all + * used statements during the session. + * {@link org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer ResetAbandonedTimer} resets the timer upon every operation + * on the connection or a statement. + * {@link org.apache.tomcat.jdbc.pool.interceptor.ConnectionState ConnectionState} caches the auto commit, read only and catalog settings to avoid round trips to the DB. + * The default value is null. + * @param jdbcInterceptors the interceptors that are used for connections. + * Example format: 'ConnectionState(useEquals=true,fast=yes);ResetAbandonedTimer' + */ + public void setJdbcInterceptors(String jdbcInterceptors); + + /** + * Returns the {@link #getJdbcInterceptors()} as an array of objects with properties and the classes. + * @return an array of interceptors that have been configured + */ + public InterceptorDefinition[] getJdbcInterceptorsAsArray(); + + + /** + * If set to true, the connection pool creates a {@link org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean} object + * that can be registered with JMX to receive notifications and state about the pool. + * The ConnectionPool object doesn't register itself, as there is no way to keep a static non changing ObjectName across JVM restarts. + * @return true if the mbean object will be created upon startup. + */ + public boolean isJmxEnabled(); + + /** + * If set to true, the connection pool creates a {@link org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean} object + * that can be registered with JMX to receive notifications and state about the pool. + * The ConnectionPool object doesn't register itself, as there is no way to keep a static non changing ObjectName across JVM restarts. + * @param jmxEnabled set to to if the mbean object should be created upon startup. + */ + public void setJmxEnabled(boolean jmxEnabled); + + /** + * Returns true if the pool sweeper is enabled for the connection pool. + * The pool sweeper is enabled if any settings that require async intervention in the pool are turned on + * + boolean result = getTimeBetweenEvictionRunsMillis()>0; + result = result && (isRemoveAbandoned() && getRemoveAbandonedTimeout()>0); + result = result || (isTestWhileIdle() && getValidationQuery()!=null); + return result; + + * + * @return true if a background thread is or will be enabled for this pool + */ + public boolean isPoolSweeperEnabled(); + + /** + * Set to true if you wish the ProxyConnection class to use String.equals instead of + * == when comparing method names. + * This property does not apply to added interceptors as those are configured individually. + * The default value is false. + * @return true if pool uses {@link String#equals(Object)} instead of == when comparing method names on {@link java.sql.Connection} methods + */ + public boolean isUseEquals(); + + /** + * Set to true if you wish the ProxyConnection class to use String.equals instead of + * == when comparing method names. + * This property does not apply to added interceptors as those are configured individually. + * The default value is false. + * @param useEquals set to true if the pool should use {@link String#equals(Object)} instead of == + * when comparing method names on {@link java.sql.Connection} methods + */ + public void setUseEquals(boolean useEquals); + + /** + * Time in milliseconds to keep this connection alive even when used. + * When a connection is returned to the pool, the pool will check to see if the + * ((now - time-when-connected) > maxAge) has been reached, and if so, + * it closes the connection rather than returning it to the pool. + * The default value is 0, which implies that connections will be left open and no + * age check will be done upon returning the connection to the pool. + * This is a useful setting for database sessions that leak memory as it ensures that the session + * will have a finite life span. + * @return the time in milliseconds a connection will be open for when used + */ + public long getMaxAge(); + + /** + * Time in milliseconds to keep this connection alive even when used. + * When a connection is returned to the pool, the pool will check to see if the + * ((now - time-when-connected) > maxAge) has been reached, and if so, + * it closes the connection rather than returning it to the pool. + * The default value is 0, which implies that connections will be left open and no + * age check will be done upon returning the connection to the pool. + * This is a useful setting for database sessions that leak memory as it ensures that the session + * will have a finite life span. + * @param maxAge the time in milliseconds a connection will be open for when used + */ + public void setMaxAge(long maxAge); + + /** + * Return true if a lock should be used when operations are performed on the connection object. + * Should be set to false unless you plan to have a background thread of your own doing idle and abandon checking + * such as JMX clients. If the pool sweeper is enabled, then the lock will automatically be used regardless of this setting. + * @return true if a lock is used. + */ + public boolean getUseLock(); + + /** + * Set to true if a lock should be used when operations are performed on the connection object. + * Should be set to false unless you plan to have a background thread of your own doing idle and abandon checking + * such as JMX clients. If the pool sweeper is enabled, then the lock will automatically be used regardless of this setting. + * @param useLock set to true if a lock should be used on connection operations + */ + public void setUseLock(boolean useLock); + + /** + * Similar to {@link #setRemoveAbandonedTimeout(int)} but instead of treating the connection + * as abandoned, and potentially closing the connection, this simply logs the warning if + * {@link #isLogAbandoned()} returns true. If this value is equal or less than 0, no suspect + * checking will be performed. Suspect checking only takes place if the timeout value is larger than 0 and + * the connection was not abandoned or if abandon check is disabled. If a connection is suspect a WARN message gets + * logged and a JMX notification gets sent once. + * @param seconds - the amount of time in seconds that has to pass before a connection is marked suspect. + */ + public void setSuspectTimeout(int seconds); + + /** + * Returns the time in seconds to pass before a connection is marked an abandoned suspect. + * Any value lesser than or equal to 0 means the check is disabled. + * @return Returns the time in seconds to pass before a connection is marked an abandoned suspect. + */ + public int getSuspectTimeout(); + + /** + * Injects a datasource that will be used to retrieve/create connections. + * If a data source is set, the {@link PoolConfiguration#getUrl()} and {@link PoolConfiguration#getDriverClassName()} methods are ignored + * and not used by the pool. If the {@link PoolConfiguration#getUsername()} and {@link PoolConfiguration#getPassword()} + * values are set, the method {@link javax.sql.DataSource#getConnection(String, String)} method will be called instead of the + * {@link javax.sql.DataSource#getConnection()} method. + * If the data source implements {@link javax.sql.XADataSource} the methods + * {@link javax.sql.XADataSource#getXAConnection()} and {@link javax.sql.XADataSource#getXAConnection(String,String)} + * will be invoked. + * @param ds the {@link javax.sql.DataSource} to be used for creating connections to be pooled. + */ + public void setDataSource(Object ds); + + /** + * Returns a datasource, if one exists that is being used to create connections. + * This method will return null if the pool is using a {@link java.sql.Driver} + * @return the {@link javax.sql.DataSource} to be used for creating connections to be pooled or null if a Driver is used. + */ + public Object getDataSource(); + + /** + * Configure the connection pool to use a DataSource according to {@link PoolConfiguration#setDataSource(Object)} + * But instead of injecting the object, specify the JNDI location. + * After a successful JNDI look, the {@link PoolConfiguration#getDataSource()} will not return null. + * @param jndiDS -the JNDI string @TODO specify the rules here. + */ + public void setDataSourceJNDI(String jndiDS); + + /** + * Returns the JNDI string configured for data source usage. + * @return the JNDI string or null if not set + */ + public String getDataSourceJNDI(); + + /** + * Returns true if the call {@link DataSource#getConnection(String, String) getConnection(username,password)} is + * allowed. This is used for when the pool is used by an application accessing multiple schemas. + * There is a performance impact turning this option on. + * @return true if {@link DataSource#getConnection(String, String) getConnection(username,password)} is honored, false if it is ignored. + */ + public boolean isAlternateUsernameAllowed(); + + /** + * Set to true if the call {@link DataSource#getConnection(String, String) getConnection(username,password)} is + * allowed and honored.. This is used for when the pool is used by an application accessing multiple schemas. + * There is a performance impact turning this option on, even when not used due to username checks. + * @param alternateUsernameAllowed - set true if {@link DataSource#getConnection(String, String) getConnection(username,password)} is honored, + * false if it is to be ignored. + */ + public void setAlternateUsernameAllowed(boolean alternateUsernameAllowed); + /** + * Set to true if you want the connection pool to commit any pending transaction when a connection is returned. + * The default value is false, as this could result in committing data. + * This parameter is only looked at if the {@link #getDefaultAutoCommit()} returns false + * @param commitOnReturn set to true if the pool should call {@link java.sql.Connection#commit()} when a connection is returned to the pool. + * Default is false + */ + public void setCommitOnReturn(boolean commitOnReturn); + + /** + * @see PoolConfiguration#setCommitOnReturn(boolean) + * @return true if the pool should commit when a connection is returned to it + */ + public boolean getCommitOnReturn(); + + /** + * Set to true if you want the connection pool to rollback any pending transaction when a connection is returned. + * The default value is false, as this could result in committing data. + * This parameter is only looked at if the {@link #getDefaultAutoCommit()} returns false + * @param rollbackOnReturn set to true if the pool should call {@link java.sql.Connection#rollback()} when a connection is returned to the pool. + * Default is false + */ + public void setRollbackOnReturn(boolean rollbackOnReturn); + + /** + * @see PoolConfiguration#setRollbackOnReturn(boolean) + * @return true if the pool should rollback when a connection is returned to it + */ + public boolean getRollbackOnReturn(); + + /** + * If set to true, the connection will be wrapped with facade that will disallow the connection to be used after + * {@link java.sql.Connection#close()} is called. If set to true, after {@link java.sql.Connection#close()} all calls except + * {@link java.sql.Connection#close()} and {@link java.sql.Connection#isClosed()} will throw an exception. + * @param useDisposableConnectionFacade true to use a facade + */ + public void setUseDisposableConnectionFacade(boolean useDisposableConnectionFacade); + /** + * Returns true if this connection pool is configured to use a connection facade to prevent re-use of connection after + * {@link java.sql.Connection#close()} has been invoked + * @return true if {@link java.sql.Connection#close()} has been invoked. + */ + public boolean getUseDisposableConnectionFacade(); + + /** + * Set to true if you wish that errors from validation should be logged as error messages. + * @param logValidationErrors set to true to log validation errors + */ + public void setLogValidationErrors(boolean logValidationErrors); + + /** + * Returns true if errors that happen during validation will be logged + * @return true if errors that happen during validation will be logged + */ + public boolean getLogValidationErrors(); + + /** + * Returns true if the pool is configured to propagate interrupt state of a thread. + * A thread waiting for a connection, can have its wait interrupted, and by default + * will clear the interrupt flag and throw a {@link PoolExhaustedException} + * @return true if the pool is configured to propagate and not clear the thread interrupt state + */ + public boolean getPropagateInterruptState(); + + /** + * Configure the pool to propagate interrupt state for interrupted threads waiting for a connection + * A thread waiting for a connection, can have its wait interrupted, and by default + * will clear the interrupt flag and throw a {@link PoolExhaustedException} + * If set to true, this behavior will change, while the {@link PoolExhaustedException} is still thrown, the threads interrupted state is still set. + * @param propagateInterruptState - set to true to not clear, but propagate, a threads interrupted state. + */ + public void setPropagateInterruptState(boolean propagateInterruptState); + + /** + * Set to true if you want to ignore error of connection creation while initializing the pool. + * Set to false if you want to fail the initialization of the pool by throwing exception. + * @param ignoreExceptionOnPreLoad set to true if you want to ignore error of connection creation while initializing the pool. + */ + public void setIgnoreExceptionOnPreLoad(boolean ignoreExceptionOnPreLoad); + + /** + * @return true to ignore exceptions + * @see PoolConfiguration#setIgnoreExceptionOnPreLoad(boolean) + */ + public boolean isIgnoreExceptionOnPreLoad(); + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolExhaustedException.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolExhaustedException.java new file mode 100644 index 0000000..9352581 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolExhaustedException.java @@ -0,0 +1,76 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.sql.SQLException; + +public class PoolExhaustedException extends SQLException { + + private static final long serialVersionUID = 3501536931777262475L; + + public PoolExhaustedException() { + } + + public PoolExhaustedException(String reason) { + super(reason); + } + + public PoolExhaustedException(Throwable cause) { + super(cause); + } + + public PoolExhaustedException(String reason, String SQLState) { + super(reason, SQLState); + } + + public PoolExhaustedException(String reason, Throwable cause) { + super(reason, cause); + } + + public PoolExhaustedException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public PoolExhaustedException(String reason, String sqlState, Throwable cause) { + super(reason, sqlState, cause); + } + + public PoolExhaustedException(String reason, String sqlState, int vendorCode, Throwable cause) { + super(reason, sqlState, vendorCode, cause); + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java new file mode 100644 index 0000000..9d64c26 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java @@ -0,0 +1,1332 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; + + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +public class PoolProperties implements PoolConfiguration, Cloneable, Serializable { + + private static final long serialVersionUID = -8519283440854213745L; + private static final Log log = LogFactory.getLog(PoolProperties.class); + + public static final int DEFAULT_MAX_ACTIVE = 100; + + protected static final AtomicInteger poolCounter = new AtomicInteger(0); + private volatile Properties dbProperties = new Properties(); + private volatile String url = null; + private volatile String driverClassName = null; + private volatile Boolean defaultAutoCommit = null; + private volatile Boolean defaultReadOnly = null; + private volatile int defaultTransactionIsolation = DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION; + private volatile String defaultCatalog = null; + private volatile String connectionProperties; + private volatile int initialSize = 10; + private volatile int maxActive = DEFAULT_MAX_ACTIVE; + private volatile int maxIdle = maxActive; + private volatile int minIdle = initialSize; + private volatile int maxWait = 30000; + private volatile String validationQuery; + private volatile int validationQueryTimeout = -1; + private volatile String validatorClassName; + private volatile Validator validator; + private volatile boolean testOnBorrow = false; + private volatile boolean testOnReturn = false; + private volatile boolean testWhileIdle = false; + private volatile int timeBetweenEvictionRunsMillis = 5000; + private volatile int numTestsPerEvictionRun; + private volatile int minEvictableIdleTimeMillis = 60000; + private volatile boolean accessToUnderlyingConnectionAllowed = true; + private volatile boolean removeAbandoned = false; + private volatile int removeAbandonedTimeout = 60; + private volatile boolean logAbandoned = false; + private volatile String name = "Tomcat Connection Pool["+(poolCounter.addAndGet(1))+"-"+System.identityHashCode(PoolProperties.class)+"]"; + private volatile String password; + private volatile String username; + private volatile long validationInterval = 3000; + private volatile boolean jmxEnabled = true; + private volatile String initSQL; + private volatile boolean testOnConnect =false; + private volatile String jdbcInterceptors=null; + private volatile boolean fairQueue = true; + private volatile boolean useEquals = true; + private volatile int abandonWhenPercentageFull = 0; + private volatile long maxAge = 0; + private volatile boolean useLock = false; + private volatile InterceptorDefinition[] interceptors = null; + private volatile int suspectTimeout = 0; + private volatile Object dataSource = null; + private volatile String dataSourceJNDI = null; + private volatile boolean alternateUsernameAllowed = false; + private volatile boolean commitOnReturn = false; + private volatile boolean rollbackOnReturn = false; + private volatile boolean useDisposableConnectionFacade = true; + private volatile boolean logValidationErrors = false; + private volatile boolean propagateInterruptState = false; + private volatile boolean ignoreExceptionOnPreLoad = false; + + /** + * {@inheritDoc} + */ + @Override + public void setAbandonWhenPercentageFull(int percentage) { + if (percentage<0) abandonWhenPercentageFull = 0; + else if (percentage>100) abandonWhenPercentageFull = 100; + else abandonWhenPercentageFull = percentage; + } + + /** + * {@inheritDoc} + */ + @Override + public int getAbandonWhenPercentageFull() { + return abandonWhenPercentageFull; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFairQueue() { + return fairQueue; + } + + /** + * {@inheritDoc} + */ + @Override + public void setFairQueue(boolean fairQueue) { + this.fairQueue = fairQueue; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAccessToUnderlyingConnectionAllowed() { + return accessToUnderlyingConnectionAllowed; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getConnectionProperties() { + return connectionProperties; + } + + /** + * {@inheritDoc} + */ + + @Override + public Properties getDbProperties() { + return dbProperties; + } + + /** + * {@inheritDoc} + */ + + @Override + public Boolean isDefaultAutoCommit() { + return defaultAutoCommit; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getDefaultCatalog() { + return defaultCatalog; + } + + /** + * {@inheritDoc} + */ + + @Override + public Boolean isDefaultReadOnly() { + return defaultReadOnly; + } + + /** + * {@inheritDoc} + */ + + @Override + public int getDefaultTransactionIsolation() { + return defaultTransactionIsolation; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getDriverClassName() { + return driverClassName; + } + + /** + * {@inheritDoc} + */ + + @Override + public int getInitialSize() { + return initialSize; + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isLogAbandoned() { + return logAbandoned; + } + + /** + * {@inheritDoc} + */ + + @Override + public int getMaxActive() { + return maxActive; + } + + /** + * {@inheritDoc} + */ + + @Override + public int getMaxIdle() { + return maxIdle; + } + + /** + * {@inheritDoc} + */ + + @Override + public int getMaxWait() { + return maxWait; + } + + /** + * {@inheritDoc} + */ + + @Override + public int getMinEvictableIdleTimeMillis() { + return minEvictableIdleTimeMillis; + } + + /** + * {@inheritDoc} + */ + + @Override + public int getMinIdle() { + return minIdle; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getName() { + return name; + } + + /** + * {@inheritDoc} + */ + + @Override + public int getNumTestsPerEvictionRun() { + return numTestsPerEvictionRun; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getPassword() { + return password; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getPoolName() { + return getName(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isRemoveAbandoned() { + return removeAbandoned; + } + + /** + * {@inheritDoc} + */ + + @Override + public int getRemoveAbandonedTimeout() { + return removeAbandonedTimeout; + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isTestOnBorrow() { + return testOnBorrow; + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isTestOnReturn() { + return testOnReturn; + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isTestWhileIdle() { + return testWhileIdle; + } + + /** + * {@inheritDoc} + */ + + @Override + public int getTimeBetweenEvictionRunsMillis() { + return timeBetweenEvictionRunsMillis; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getUrl() { + return url; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getUsername() { + return username; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getValidationQuery() { + return validationQuery; + } + + /** + * {@inheritDoc} + */ + @Override + public int getValidationQueryTimeout() { + return validationQueryTimeout; + } + + /** + * {@inheritDoc} + */ + @Override + public void setValidationQueryTimeout(int validationQueryTimeout) { + this.validationQueryTimeout = validationQueryTimeout; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getValidatorClassName() { + return validatorClassName; + } + + /** + * {@inheritDoc} + */ + + @Override + public Validator getValidator() { + return validator; + } + + /** + * {@inheritDoc} + */ + @Override + public void setValidator(Validator validator) { + this.validator = validator; + if (validator!=null) { + this.validatorClassName = validator.getClass().getName(); + } else { + this.validatorClassName = null; + } + } + + + /** + * {@inheritDoc} + */ + + @Override + public long getValidationInterval() { + return validationInterval; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getInitSQL() { + return initSQL; + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isTestOnConnect() { + return testOnConnect; + } + + /** + * {@inheritDoc} + */ + + @Override + public String getJdbcInterceptors() { + return jdbcInterceptors; + } + + /** + * {@inheritDoc} + */ + + @Override + public InterceptorDefinition[] getJdbcInterceptorsAsArray() { + if (interceptors == null) { + if (jdbcInterceptors==null) { + interceptors = new InterceptorDefinition[0]; + } else { + String[] interceptorValues = jdbcInterceptors.split(";"); + InterceptorDefinition[] definitions = new InterceptorDefinition[interceptorValues.length+1]; + //always add the trap interceptor to the mix + definitions[0] = new InterceptorDefinition(TrapException.class); + for (int i=0; i validatorClass = (Class)ClassLoaderUtil.loadClass( + className, + PoolProperties.class.getClassLoader(), + Thread.currentThread().getContextClassLoader() + ); + validator = validatorClass.newInstance(); + } catch (ClassNotFoundException e) { + log.warn("The class "+className+" cannot be found.", e); + } catch (ClassCastException e) { + log.warn("The class "+className+" does not implement the Validator interface.", e); + } catch (InstantiationException e) { + log.warn("An object of class "+className+" cannot be instantiated. Make sure that "+ + "it includes an implicit or explicit no-arg constructor.", e); + } catch (IllegalAccessException e) { + log.warn("The class "+className+" or its no-arg constructor are inaccessible.", e); + } + } + + /** + * {@inheritDoc} + */ + + @Override + public void setInitSQL(String initSQL) { + this.initSQL = initSQL!=null && initSQL.trim().length()>0 ? initSQL : null; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setTestOnConnect(boolean testOnConnect) { + this.testOnConnect = testOnConnect; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setJdbcInterceptors(String jdbcInterceptors) { + this.jdbcInterceptors = jdbcInterceptors; + this.interceptors = null; + } + + + @Override + public String toString() { + StringBuilder buf = new StringBuilder("ConnectionPool["); + try { + String[] fields = DataSourceFactory.ALL_PROPERTIES; + for (String field: fields) { + final String[] prefix = new String[] {"get","is"}; + for (int j=0; j0; + boolean result = timer && (isRemoveAbandoned() && getRemoveAbandonedTimeout()>0); + result = result || (timer && getSuspectTimeout()>0); + result = result || (timer && isTestWhileIdle() && getValidationQuery()!=null); + result = result || (timer && getMinEvictableIdleTimeMillis()>0); + return result; + } + + + public static class InterceptorDefinition implements Serializable { + private static final long serialVersionUID = 1L; + protected String className; + protected Map properties = new HashMap<>(); + protected volatile Class clazz = null; + public InterceptorDefinition(String className) { + this.className = className; + } + + public InterceptorDefinition(Class cl) { + this(cl.getName()); + clazz = cl; + } + + public String getClassName() { + return className; + } + public void addProperty(String name, String value) { + InterceptorProperty p = new InterceptorProperty(name,value); + addProperty(p); + } + + public void addProperty(InterceptorProperty p) { + properties.put(p.getName(), p); + } + + public Map getProperties() { + return properties; + } + + @SuppressWarnings("unchecked") + public Class getInterceptorClass() throws ClassNotFoundException { + if (clazz==null) { + if (getClassName().indexOf('.')<0) { + if (log.isDebugEnabled()) { + log.debug("Loading interceptor class:"+PoolConfiguration.PKG_PREFIX+getClassName()); + } + clazz = ClassLoaderUtil.loadClass( + PoolConfiguration.PKG_PREFIX+getClassName(), + PoolProperties.class.getClassLoader(), + Thread.currentThread().getContextClassLoader() + ); + } else { + if (log.isDebugEnabled()) { + log.debug("Loading interceptor class:"+getClassName()); + } + clazz = ClassLoaderUtil.loadClass( + getClassName(), + PoolProperties.class.getClassLoader(), + Thread.currentThread().getContextClassLoader() + ); + } + } + return (Class)clazz; + } + } + + public static class InterceptorProperty implements Serializable { + private static final long serialVersionUID = 1L; + String name; + String value; + public InterceptorProperty(String name, String value) { + assert(name!=null); + this.name = name; + this.value = value; + } + public String getName() { + return name; + } + public String getValue() { + return value; + } + + public boolean getValueAsBoolean(boolean def) { + if (value==null) return def; + if ("true".equals(value)) return true; + if ("false".equals(value)) return false; + return def; + } + + public int getValueAsInt(int def) { + if (value==null) return def; + try { + int v = Integer.parseInt(value); + return v; + }catch (NumberFormatException nfe) { + return def; + } + } + + public long getValueAsLong(long def) { + if (value==null) return def; + try { + return Long.parseLong(value); + }catch (NumberFormatException nfe) { + return def; + } + } + + public byte getValueAsByte(byte def) { + if (value==null) return def; + try { + return Byte.parseByte(value); + }catch (NumberFormatException nfe) { + return def; + } + } + + public short getValueAsShort(short def) { + if (value==null) return def; + try { + return Short.parseShort(value); + }catch (NumberFormatException nfe) { + return def; + } + } + + public float getValueAsFloat(float def) { + if (value==null) return def; + try { + return Float.parseFloat(value); + }catch (NumberFormatException nfe) { + return def; + } + } + + public double getValueAsDouble(double def) { + if (value==null) return def; + try { + return Double.parseDouble(value); + }catch (NumberFormatException nfe) { + return def; + } + } + + public char getValueAschar(char def) { + if (value==null) return def; + try { + return value.charAt(0); + }catch (StringIndexOutOfBoundsException nfe) { + return def; + } + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o==this) return true; + if (o instanceof InterceptorProperty) { + InterceptorProperty other = (InterceptorProperty)o; + return other.name.equals(this.name); + } + return false; + } + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isUseEquals() { + return useEquals; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setUseEquals(boolean useEquals) { + this.useEquals = useEquals; + } + + /** + * {@inheritDoc} + */ + + @Override + public long getMaxAge() { + return maxAge; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMaxAge(long maxAge) { + this.maxAge = maxAge; + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean getUseLock() { + return useLock; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setUseLock(boolean useLock) { + this.useLock = useLock; + } + + + /** + * {@inheritDoc} + */ + @Override + public void setDataSource(Object ds) { + if (ds instanceof DataSourceProxy) { + throw new IllegalArgumentException("Layered pools are not allowed."); + } + this.dataSource = ds; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getDataSource() { + return dataSource; + } + + + /** + * {@inheritDoc} + */ + @Override + public void setDataSourceJNDI(String jndiDS) { + this.dataSourceJNDI = jndiDS; + } + + /** + * {@inheritDoc} + */ + @Override + public String getDataSourceJNDI() { + return this.dataSourceJNDI; + } + + + public static Properties getProperties(String propText, Properties props) { + if (props==null) props = new Properties(); + if (propText != null) { + try { + props.load(new ByteArrayInputStream(propText.replace(';', '\n').getBytes())); + }catch (IOException x) { + throw new RuntimeException(x); + } + } + return props; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAlternateUsernameAllowed() { + return alternateUsernameAllowed; + } + + /** + * {@inheritDoc} + */ + @Override + public void setAlternateUsernameAllowed(boolean alternateUsernameAllowed) { + this.alternateUsernameAllowed = alternateUsernameAllowed; + } + + + /** + * {@inheritDoc} + */ + @Override + public void setCommitOnReturn(boolean commitOnReturn) { + this.commitOnReturn = commitOnReturn; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getCommitOnReturn() { + return this.commitOnReturn; + } + + /** + * {@inheritDoc} + */ + @Override + public void setRollbackOnReturn(boolean rollbackOnReturn) { + this.rollbackOnReturn = rollbackOnReturn; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getRollbackOnReturn() { + return this.rollbackOnReturn; + } + + /** + * {@inheritDoc} + */ + @Override + public void setUseDisposableConnectionFacade(boolean useDisposableConnectionFacade) { + this.useDisposableConnectionFacade = useDisposableConnectionFacade; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getUseDisposableConnectionFacade() { + return useDisposableConnectionFacade; + } + + /** + * {@inheritDoc} + */ + @Override + public void setLogValidationErrors(boolean logValidationErrors) { + this.logValidationErrors = logValidationErrors; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getLogValidationErrors() { + return this.logValidationErrors; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getPropagateInterruptState() { + return propagateInterruptState; + } + + /** + * {@inheritDoc} + */ + @Override + public void setPropagateInterruptState(boolean propagateInterruptState) { + this.propagateInterruptState = propagateInterruptState; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isIgnoreExceptionOnPreLoad() { + return ignoreExceptionOnPreLoad; + } + + /** + * {@inheritDoc} + */ + @Override + public void setIgnoreExceptionOnPreLoad(boolean ignoreExceptionOnPreLoad) { + this.ignoreExceptionOnPreLoad = ignoreExceptionOnPreLoad; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + // TODO Auto-generated method stub + return super.clone(); + } + + + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolUtilities.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolUtilities.java new file mode 100644 index 0000000..cd0d3b2 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolUtilities.java @@ -0,0 +1,58 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.util.Properties; + +public class PoolUtilities { + + public static final String PROP_USER = "user"; + + public static final String PROP_PASSWORD = "password"; + + public static Properties clone(Properties p) { + Properties c = new Properties(); + c.putAll(p); + return c; + } + + public static Properties cloneWithoutPassword(Properties p) { + Properties result = clone(p); + result.remove(PROP_PASSWORD); + return result; + } +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java new file mode 100644 index 0000000..b704c7f --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java @@ -0,0 +1,795 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + + +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; + +import com.mysql.jdbc.Driver; + +/** + * Represents a pooled connection + * and holds a reference to the {@link java.sql.Connection} object + * @version 1.0 + */ +public class PooledConnection { + /** + * Logger + */ + private static final Log log = LogFactory.getLog(PooledConnection.class); + + public static final String PROP_USER = PoolUtilities.PROP_USER; + + public static final String PROP_PASSWORD = PoolUtilities.PROP_PASSWORD; + + /** + * Validate when connection is borrowed flag + */ + public static final int VALIDATE_BORROW = 1; + /** + * Validate when connection is returned flag + */ + public static final int VALIDATE_RETURN = 2; + /** + * Validate when connection is idle flag + */ + public static final int VALIDATE_IDLE = 3; + /** + * Validate when connection is initialized flag + */ + public static final int VALIDATE_INIT = 4; + /** + * The properties for the connection pool + */ + protected PoolConfiguration poolProperties; + /** + * The underlying database connection + */ + private volatile java.sql.Connection connection; + + /** + * If using a XAConnection underneath. + */ + protected volatile javax.sql.XAConnection xaConnection; + /** + * When we track abandon traces, this string holds the thread dump + */ + private String abandonTrace = null; + /** + * Timestamp the connection was last 'touched' by the pool + */ + private volatile long timestamp; + /** + * Lock for this connection only + */ + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false); + /** + * Set to true if this connection has been discarded by the pool + */ + private volatile boolean discarded = false; + /** + * The Timestamp when the last time the connect() method was called successfully + */ + private volatile long lastConnected = -1; + /** + * timestamp to keep track of validation intervals + */ + private volatile long lastValidated = System.currentTimeMillis(); + /** + * The parent + */ + protected ConnectionPool parent; + + private HashMap attributes = new HashMap<>(); + + private volatile long connectionVersion=0; + + /** + * Weak reference to cache the list of interceptors for this connection + * so that we don't create a new list of interceptors each time we borrow + * the connection + */ + private volatile JdbcInterceptor handler = null; + + private AtomicBoolean released = new AtomicBoolean(false); + + private volatile boolean suspect = false; + + private java.sql.Driver driver = null; + + /** + * Constructor + * @param prop - pool properties + * @param parent - the parent connection pool + */ + public PooledConnection(PoolConfiguration prop, ConnectionPool parent) { + poolProperties = prop; + this.parent = parent; + connectionVersion = parent.getPoolVersion(); + } + + public long getConnectionVersion() { + return connectionVersion; + } + + /** + * @deprecated use {@link #shouldForceReconnect(String, String)} + * method kept since it was public, to avoid changing interface. + * @param username The user name + * @param password The password + * @return trueif the pool does not need to reconnect + */ + @Deprecated + public boolean checkUser(String username, String password) { + return !shouldForceReconnect(username, password); + } + + /** + * Returns true if we must force reconnect based on credentials passed in. + * Returns false if {@link PoolConfiguration#isAlternateUsernameAllowed()} method returns false. + * Returns false if the username/password has not changed since this connection was connected + * @param username the username you wish to connect with, pass in null to accept the default username from {@link PoolConfiguration#getUsername()} + * @param password the password you wish to connect with, pass in null to accept the default username from {@link org.apache.tomcat.jdbc.pool.PoolConfiguration#getPassword()} + * @return true is the pool must reconnect + */ + public boolean shouldForceReconnect(String username, String password) { + + if (!getPoolProperties().isAlternateUsernameAllowed()) return false; + + if (username==null) username = poolProperties.getUsername(); + if (password==null) password = poolProperties.getPassword(); + + String storedUsr = (String)getAttributes().get(PROP_USER); + String storedPwd = (String)getAttributes().get(PROP_PASSWORD); + + boolean noChangeInCredentials = (username==null && storedUsr==null); + noChangeInCredentials = (noChangeInCredentials || (username!=null && username.equals(storedUsr))); + + noChangeInCredentials = noChangeInCredentials && ((password==null && storedPwd==null) || (password!=null && password.equals(storedPwd))); + + if (username==null) getAttributes().remove(PROP_USER); else getAttributes().put(PROP_USER, username); + if (password==null) getAttributes().remove(PROP_PASSWORD); else getAttributes().put(PROP_PASSWORD, password); + + return !noChangeInCredentials; + } + + /** + * Connects the underlying connection to the database. + * @throws SQLException if the method {@link #release()} has been called. + * @throws SQLException if driver instantiation fails + * @throws SQLException if a call to {@link java.sql.Driver#connect(String, java.util.Properties)} fails. + * @throws SQLException if default properties are configured and a call to + * {@link java.sql.Connection#setAutoCommit(boolean)}, {@link java.sql.Connection#setCatalog(String)}, + * {@link java.sql.Connection#setTransactionIsolation(int)} or {@link java.sql.Connection#setReadOnly(boolean)} fails. + */ + public void connect() throws SQLException { + if (released.get()) throw new SQLException("A connection once released, can't be reestablished."); + if (connection != null) { + try { + this.disconnect(false); + } catch (Exception x) { + log.debug("Unable to disconnect previous connection.", x); + } //catch + } //end if + if (poolProperties.getDataSource()==null && poolProperties.getDataSourceJNDI()!=null) { + //TODO lookup JNDI name + } + + if (poolProperties.getDataSource()!=null) { + connectUsingDataSource(); + } else { + connectUsingDriver(); + } + + //set up the default state, unless we expect the interceptor to do it + if (poolProperties.getJdbcInterceptors()==null || poolProperties.getJdbcInterceptors().indexOf(ConnectionState.class.getName())<0 || + poolProperties.getJdbcInterceptors().indexOf(ConnectionState.class.getSimpleName())<0) { + if (poolProperties.getDefaultTransactionIsolation()!=DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION) connection.setTransactionIsolation(poolProperties.getDefaultTransactionIsolation()); + if (poolProperties.getDefaultReadOnly()!=null) connection.setReadOnly(poolProperties.getDefaultReadOnly().booleanValue()); + if (poolProperties.getDefaultAutoCommit()!=null) connection.setAutoCommit(poolProperties.getDefaultAutoCommit().booleanValue()); + if (poolProperties.getDefaultCatalog()!=null) connection.setCatalog(poolProperties.getDefaultCatalog()); + } + this.discarded = false; + this.lastConnected = System.currentTimeMillis(); + } + + protected void connectUsingDataSource() throws SQLException { + String usr = null; + String pwd = null; + if (getAttributes().containsKey(PROP_USER)) { + usr = (String) getAttributes().get(PROP_USER); + } else { + usr = poolProperties.getUsername(); + getAttributes().put(PROP_USER, usr); + } + if (getAttributes().containsKey(PROP_PASSWORD)) { + pwd = (String) getAttributes().get(PROP_PASSWORD); + } else { + pwd = poolProperties.getPassword(); + getAttributes().put(PROP_PASSWORD, pwd); + } + if (poolProperties.getDataSource() instanceof javax.sql.XADataSource) { + javax.sql.XADataSource xds = (javax.sql.XADataSource)poolProperties.getDataSource(); + if (usr!=null && pwd!=null) { + xaConnection = xds.getXAConnection(usr, pwd); + connection = xaConnection.getConnection(); + } else { + xaConnection = xds.getXAConnection(); + connection = xaConnection.getConnection(); + } + } else if (poolProperties.getDataSource() instanceof javax.sql.DataSource){ + javax.sql.DataSource ds = (javax.sql.DataSource)poolProperties.getDataSource(); + if (usr!=null && pwd!=null) { + connection = ds.getConnection(usr, pwd); + } else { + connection = ds.getConnection(); + } + } else if (poolProperties.getDataSource() instanceof javax.sql.ConnectionPoolDataSource){ + javax.sql.ConnectionPoolDataSource ds = (javax.sql.ConnectionPoolDataSource)poolProperties.getDataSource(); + if (usr!=null && pwd!=null) { + connection = ds.getPooledConnection(usr, pwd).getConnection(); + } else { + connection = ds.getPooledConnection().getConnection(); + } + } else { + throw new SQLException("DataSource is of unknown class:"+(poolProperties.getDataSource()!=null?poolProperties.getDataSource().getClass():"null")); + } + } + protected void connectUsingDriver() throws SQLException { + + try { + Class.forName("com.mysql.jdbc.Driver") ; + Driver dr = new com.mysql.jdbc.Driver(); + if(dr == null) + log.warn("Driver NOT CREATED"); + } catch (ClassNotFoundException e) { + log.warn("Driver NOT CREATED", e); + } + + try { + if (driver==null) { + if (log.isDebugEnabled()) { + log.debug("Instantiating driver using class: "+poolProperties.getDriverClassName()+" [url="+poolProperties.getUrl()+"]"); + } + if (poolProperties.getDriverClassName()==null) { + //rely on DriverManager + log.warn("Not loading a JDBC driver as driverClassName property is null."); + } else { + driver = (java.sql.Driver) + ClassLoaderUtil.loadClass( + poolProperties.getDriverClassName(), + PooledConnection.class.getClassLoader(), + Thread.currentThread().getContextClassLoader() + ).newInstance(); + } + } + } catch (java.lang.Exception cn) { + if (log.isDebugEnabled()) { + log.debug("Unable to instantiate JDBC driver.", cn); + } + SQLException ex = new SQLException(cn.getMessage()); + ex.initCause(cn); + throw ex; + } + String driverURL = poolProperties.getUrl(); + String usr = null; + String pwd = null; + if (getAttributes().containsKey(PROP_USER)) { + usr = (String) getAttributes().get(PROP_USER); + } else { + usr = poolProperties.getUsername(); + getAttributes().put(PROP_USER, usr); + } + if (getAttributes().containsKey(PROP_PASSWORD)) { + pwd = (String) getAttributes().get(PROP_PASSWORD); + } else { + pwd = poolProperties.getPassword(); + getAttributes().put(PROP_PASSWORD, pwd); + } + Properties properties = PoolUtilities.clone(poolProperties.getDbProperties()); + if (usr != null) properties.setProperty(PROP_USER, usr); + if (pwd != null) properties.setProperty(PROP_PASSWORD, pwd); + + try { + if (driver==null) { + connection = DriverManager.getConnection(driverURL, properties); + } else { + connection = driver.connect(driverURL, properties); + } + } catch (Exception x) { + if (log.isDebugEnabled()) { + log.debug("Unable to connect to database.", x); + } + if (parent.jmxPool!=null) { + parent.jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_CONNECT, + ConnectionPool.getStackTrace(x)); + } + if (x instanceof SQLException) { + throw (SQLException)x; + } else { + SQLException ex = new SQLException(x.getMessage()); + ex.initCause(x); + throw ex; + } + } + if (connection==null) { + throw new SQLException("Driver:"+driver+" returned null for URL:"+driverURL); + } + } + + /** + * + * @return true if connect() was called successfully and disconnect has not yet been called + */ + public boolean isInitialized() { + return connection!=null; + } + + /** + * Returns true if the connection has been connected more than + * {@link PoolConfiguration#getMaxAge()} milliseconds. false otherwise. + * @return Returns true if the connection has been connected more than + * {@link PoolConfiguration#getMaxAge()} milliseconds. false otherwise. + */ + public boolean isMaxAgeExpired() { + if (getPoolProperties().getMaxAge()>0 ) { + return (System.currentTimeMillis() - getLastConnected()) > getPoolProperties().getMaxAge(); + } else { + return false; + } + } + /** + * Issues a call to {@link #disconnect(boolean)} with the argument false followed by a call to + * {@link #connect()} + * @throws SQLException if the call to {@link #connect()} fails. + */ + public void reconnect() throws SQLException { + this.disconnect(false); + this.connect(); + } //reconnect + + /** + * Disconnects the connection. All exceptions are logged using debug level. + * @param finalize if set to true, a call to {@link ConnectionPool#finalize(PooledConnection)} is called. + */ + private void disconnect(boolean finalize) { + if (isDiscarded() && connection == null) { + return; + } + setDiscarded(true); + if (connection != null) { + try { + parent.disconnectEvent(this, finalize); + if (xaConnection == null) { + connection.close(); + } else { + xaConnection.close(); + } + }catch (Exception ignore) { + if (log.isDebugEnabled()) { + log.debug("Unable to close underlying SQL connection",ignore); + } + } + } + connection = null; + xaConnection = null; + lastConnected = -1; + if (finalize) parent.finalize(this); + } + + +//============================================================================ +// +//============================================================================ + + /** + * Returns abandon timeout in milliseconds + * @return abandon timeout in milliseconds + */ + public long getAbandonTimeout() { + if (poolProperties.getRemoveAbandonedTimeout() <= 0) { + return Long.MAX_VALUE; + } else { + return poolProperties.getRemoveAbandonedTimeout() * 1000L; + } //end if + } + + /** + * Returns true if the connection pool is configured + * to do validation for a certain action. + * @param action The validation action + */ + private boolean doValidate(int action) { + if (action == PooledConnection.VALIDATE_BORROW && + poolProperties.isTestOnBorrow()) + return true; + else if (action == PooledConnection.VALIDATE_RETURN && + poolProperties.isTestOnReturn()) + return true; + else if (action == PooledConnection.VALIDATE_IDLE && + poolProperties.isTestWhileIdle()) + return true; + else if (action == PooledConnection.VALIDATE_INIT && + poolProperties.isTestOnConnect()) + return true; + else if (action == PooledConnection.VALIDATE_INIT && + poolProperties.getInitSQL()!=null) + return true; + else + return false; + } + + /** + * Returns true if the object is still valid. if not + * the pool will call the getExpiredAction() and follow up with one + * of the four expired methods + * @param validateAction The value + * @return true if the connection is valid + */ + public boolean validate(int validateAction) { + return validate(validateAction,null); + } + + /** + * Validates a connection. + * @param validateAction the action used. One of {@link #VALIDATE_BORROW}, {@link #VALIDATE_IDLE}, + * {@link #VALIDATE_INIT} or {@link #VALIDATE_RETURN} + * @param sql the SQL to be used during validation. If the {@link PoolConfiguration#setInitSQL(String)} has been called with a non null + * value and the action is {@link #VALIDATE_INIT} the init SQL will be used for validation. + * + * @return true if the connection was validated successfully. It returns true even if validation was not performed, such as when + * {@link PoolConfiguration#setValidationInterval(long)} has been called with a positive value. + *

+ * false if the validation failed. The caller should close the connection if false is returned since a session could have been left in + * an unknown state during initialization. + */ + public boolean validate(int validateAction,String sql) { + if (this.isDiscarded()) { + return false; + } + + if (!doValidate(validateAction)) { + //no validation required, no init sql and props not set + return true; + } + + //Don't bother validating if already have recently enough + long now = System.currentTimeMillis(); + if (validateAction!=VALIDATE_INIT && + poolProperties.getValidationInterval() > 0 && + (now - this.lastValidated) < + poolProperties.getValidationInterval()) { + return true; + } + + if (poolProperties.getValidator() != null) { + if (poolProperties.getValidator().validate(connection, validateAction)) { + this.lastValidated = now; + return true; + } else { + if (getPoolProperties().getLogValidationErrors()) { + log.error("Custom validation through "+poolProperties.getValidator()+" failed."); + } + return false; + } + } + + String query = sql; + + if (validateAction == VALIDATE_INIT && poolProperties.getInitSQL() != null) { + query = poolProperties.getInitSQL(); + } + + if (query == null) { + query = poolProperties.getValidationQuery(); + } + + if (query == null) { + int validationQueryTimeout = poolProperties.getValidationQueryTimeout(); + if (validationQueryTimeout < 0) validationQueryTimeout = 0; + try { + if (connection.isValid(validationQueryTimeout)) { + this.lastValidated = now; + return true; + } else { + if (getPoolProperties().getLogValidationErrors()) { + log.error("isValid() returned false."); + } + return false; + } + } catch (SQLException e) { + if (getPoolProperties().getLogValidationErrors()) { + log.error("isValid() failed.", e); + } else if (log.isDebugEnabled()) { + log.debug("isValid() failed.", e); + } + return false; + } + } + + Statement stmt = null; + try { + stmt = connection.createStatement(); + + int validationQueryTimeout = poolProperties.getValidationQueryTimeout(); + if (validationQueryTimeout > 0) { + stmt.setQueryTimeout(validationQueryTimeout); + } + + stmt.execute(query); + stmt.close(); + this.lastValidated = now; + return true; + } catch (Exception ex) { + if (getPoolProperties().getLogValidationErrors()) { + log.warn("SQL Validation error", ex); + } else if (log.isDebugEnabled()) { + log.debug("Unable to validate object:",ex); + } + if (stmt!=null) + try { stmt.close();} catch (Exception ignore2){/*NOOP*/} + } + return false; + } //validate + + /** + * The time limit for how long the object + * can remain unused before it is released + * @return {@link PoolConfiguration#getMinEvictableIdleTimeMillis()} + */ + public long getReleaseTime() { + return this.poolProperties.getMinEvictableIdleTimeMillis(); + } + + /** + * This method is called if (Now - timeCheckedIn > getReleaseTime()) + * This method disconnects the connection, logs an error in debug mode if it happens + * then sets the {@link #released} flag to false. Any attempts to connect this cached object again + * will fail per {@link #connect()} + * The connection pool uses the atomic return value to decrement the pool size counter. + * @return true if this is the first time this method has been called. false if this method has been called before. + */ + public boolean release() { + try { + disconnect(true); + } catch (Exception x) { + if (log.isDebugEnabled()) { + log.debug("Unable to close SQL connection",x); + } + } + return released.compareAndSet(false, true); + + } + + /** + * The pool will set the stack trace when it is check out and + * checked in + * @param trace the stack trace for this connection + */ + + public void setStackTrace(String trace) { + abandonTrace = trace; + } + + /** + * Returns the stack trace from when this connection was borrowed. Can return null if no stack trace was set. + * @return the stack trace or null of no trace was set + */ + public String getStackTrace() { + return abandonTrace; + } + + /** + * Sets a timestamp on this connection. A timestamp usually means that some operation + * performed successfully. + * @param timestamp the timestamp as defined by {@link System#currentTimeMillis()} + */ + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + setSuspect(false); + } + + + public boolean isSuspect() { + return suspect; + } + + public void setSuspect(boolean suspect) { + this.suspect = suspect; + } + + /** + * An interceptor can call this method with the value true, and the connection will be closed when it is returned to the pool. + * @param discarded - only valid value is true + * @throws IllegalStateException if this method is called with the value false and the value true has already been set. + */ + public void setDiscarded(boolean discarded) { + if (this.discarded && !discarded) throw new IllegalStateException("Unable to change the state once the connection has been discarded"); + this.discarded = discarded; + } + + /** + * Set the timestamp the connection was last validated. + * This flag is used to keep track when we are using a {@link PoolConfiguration#setValidationInterval(long) validation-interval}. + * @param lastValidated a timestamp as defined by {@link System#currentTimeMillis()} + */ + public void setLastValidated(long lastValidated) { + this.lastValidated = lastValidated; + } + + /** + * Sets the pool configuration for this connection and connection pool. + * Object is shared with the {@link ConnectionPool} + * @param poolProperties The pool properties + */ + public void setPoolProperties(PoolConfiguration poolProperties) { + this.poolProperties = poolProperties; + } + + /** + * Return the timestamps of last pool action. Timestamps are typically set when connections + * are borrowed from the pool. It is used to keep track of {@link PoolConfiguration#setRemoveAbandonedTimeout(int) abandon-timeouts}. + * This timestamp can also be reset by the {@link org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer#invoke(Object, java.lang.reflect.Method, Object[])} + * @return the timestamp of the last pool action as defined by {@link System#currentTimeMillis()} + */ + public long getTimestamp() { + return timestamp; + } + + /** + * Returns the discarded flag. + * @return the discarded flag. If the value is true, + * either {@link #disconnect(boolean)} has been called or it will be called when the connection is returned to the pool. + */ + public boolean isDiscarded() { + return discarded; + } + + /** + * Returns the timestamp of the last successful validation query execution. + * @return the timestamp of the last successful validation query execution as defined by {@link System#currentTimeMillis()} + */ + public long getLastValidated() { + return lastValidated; + } + + /** + * Returns the configuration for this connection and pool + * @return the configuration for this connection and pool + */ + public PoolConfiguration getPoolProperties() { + return poolProperties; + } + + /** + * Locks the connection only if either {@link PoolConfiguration#isPoolSweeperEnabled()} or + * {@link PoolConfiguration#getUseLock()} return true. The per connection lock ensures thread safety is + * multiple threads are performing operations on the connection. + * Otherwise this is a noop for performance + */ + public void lock() { + if (poolProperties.getUseLock() || this.poolProperties.isPoolSweeperEnabled()) { + //optimized, only use a lock when there is concurrency + lock.writeLock().lock(); + } + } + + /** + * Unlocks the connection only if the sweeper is enabled + * Otherwise this is a noop for performance + */ + public void unlock() { + if (poolProperties.getUseLock() || this.poolProperties.isPoolSweeperEnabled()) { + //optimized, only use a lock when there is concurrency + lock.writeLock().unlock(); + } + } + + /** + * Returns the underlying connection + * @return the underlying JDBC connection as it was returned from the JDBC driver + * @see javax.sql.PooledConnection#getConnection() + */ + public java.sql.Connection getConnection() { + return this.connection; + } + + /** + * Returns the underlying XA connection + * @return the underlying XA connection as it was returned from the Datasource + */ + public javax.sql.XAConnection getXAConnection() { + return this.xaConnection; + } + + + /** + * Returns the timestamp of when the connection was last connected to the database. + * ie, a successful call to {@link java.sql.Driver#connect(String, java.util.Properties)}. + * @return the timestamp when this connection was created as defined by {@link System#currentTimeMillis()} + */ + public long getLastConnected() { + return lastConnected; + } + + /** + * Returns the first handler in the interceptor chain + * @return the first interceptor for this connection + */ + public JdbcInterceptor getHandler() { + return handler; + } + + public void setHandler(JdbcInterceptor handler) { + if (this.handler!=null && this.handler!=handler) { + JdbcInterceptor interceptor = this.handler; + while (interceptor!=null) { + interceptor.reset(null, null); + interceptor = interceptor.getNext(); + }//while + }//end if + this.handler = handler; + } + + @Override + public String toString() { + return "PooledConnection["+(connection!=null?connection.toString():"null")+"]"; + } + + /** + * Returns true if this connection has been released and wont be reused. + * @return true if the method {@link #release()} has been called + */ + public boolean isReleased() { + return released.get(); + } + + public HashMap getAttributes() { + return attributes; + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java new file mode 100644 index 0000000..0a67e8e --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java @@ -0,0 +1,176 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.SQLException; + +import javax.sql.XAConnection; +/** + * A ProxyConnection object is the bottom most interceptor that wraps an object of type + * {@link PooledConnection}. The ProxyConnection intercepts three methods: + *

    + *
  • {@link java.sql.Connection#close()} - returns the connection to the pool. May be called multiple times.
  • + *
  • {@link java.lang.Object#toString()} - returns a custom string for this object
  • + *
  • {@link javax.sql.PooledConnection#getConnection()} - returns the underlying connection
  • + *
+ * By default method comparisons is done on a String reference level, unless the {@link PoolConfiguration#setUseEquals(boolean)} has been called + * with a true argument. + */ +public class ProxyConnection extends JdbcInterceptor { + + protected PooledConnection connection = null; + + protected ConnectionPool pool = null; + + public PooledConnection getConnection() { + return connection; + } + + public void setConnection(PooledConnection connection) { + this.connection = connection; + } + + public ConnectionPool getPool() { + return pool; + } + + public void setPool(ConnectionPool pool) { + this.pool = pool; + } + + protected ProxyConnection(ConnectionPool parent, PooledConnection con, + boolean useEquals) { + pool = parent; + connection = con; + setUseEquals(useEquals); + } + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + this.pool = parent; + this.connection = con; + } + + public boolean isWrapperFor(Class iface) { + if (iface == XAConnection.class && connection.getXAConnection()!=null) { + return true; + } else { + return (iface.isInstance(connection.getConnection())); + } + } + + + public Object unwrap(Class iface) throws SQLException { + if (iface == PooledConnection.class) { + return connection; + }else if (iface == XAConnection.class) { + return connection.getXAConnection(); + } else if (isWrapperFor(iface)) { + return connection.getConnection(); + } else { + throw new SQLException("Not a wrapper of "+iface.getName()); + } + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (compare(ISCLOSED_VAL,method)) { + return Boolean.valueOf(isClosed()); + } + if (compare(CLOSE_VAL,method)) { + if (connection==null) return null; //noop for already closed. + PooledConnection poolc = this.connection; + this.connection = null; + pool.returnConnection(poolc); + return null; + } else if (compare(TOSTRING_VAL,method)) { + return this.toString(); + } else if (compare(GETCONNECTION_VAL,method) && connection!=null) { + return connection.getConnection(); + } else if (method.getDeclaringClass().equals(XAConnection.class)) { + try { + return method.invoke(connection.getXAConnection(),args); + }catch (Throwable t) { + if (t instanceof InvocationTargetException) { + throw t.getCause() != null ? t.getCause() : t; + } else { + throw t; + } + } + } + if (isClosed()) throw new SQLException("Connection has already been closed."); + if (compare(UNWRAP_VAL,method)) { + return unwrap((Class)args[0]); + } else if (compare(ISWRAPPERFOR_VAL,method)) { + return Boolean.valueOf(this.isWrapperFor((Class)args[0])); + } + try { + PooledConnection poolc = connection; + if (poolc!=null) { + return method.invoke(poolc.getConnection(),args); + } else { + throw new SQLException("Connection has already been closed."); + } + }catch (Throwable t) { + if (t instanceof InvocationTargetException) { + throw t.getCause() != null ? t.getCause() : t; + } else { + throw t; + } + } + } + + public boolean isClosed() { + return connection==null || connection.isDiscarded(); + } + + public PooledConnection getDelegateConnection() { + return connection; + } + + public ConnectionPool getParentPool() { + return pool; + } + + @Override + public String toString() { + return "ProxyConnection["+(connection!=null?connection.toString():"null")+"]"; + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/TrapException.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/TrapException.java new file mode 100644 index 0000000..5c597f3 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/TrapException.java @@ -0,0 +1,101 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomcat.jdbc.pool; + + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.SQLException; +/** + * Interceptor that traps any unhandled exception types and throws an exception that has been declared by the method + * called, or throw a SQLException if it is declared. + * If the caught exception is not declared, and the method doesn't throw SQLException, then this interceptor will + * throw a RuntimeException + * + */ +public class TrapException extends JdbcInterceptor { + + + public TrapException() { + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + try { + return super.invoke(proxy, method, args); + }catch (Exception t) { + Throwable exception = t; + if (t instanceof InvocationTargetException && t.getCause() != null) { + exception = t.getCause(); + if (exception instanceof Error) { + throw exception; + } + } + Class exceptionClass = exception.getClass(); + if (!isDeclaredException(method, exceptionClass)) { + if (isDeclaredException(method,SQLException.class)) { + SQLException sqlx = new SQLException("Uncaught underlying exception."); + sqlx.initCause(exception); + exception = sqlx; + } else { + RuntimeException rx = new RuntimeException("Uncaught underlying exception."); + rx.initCause(exception); + exception = rx; + } + } + throw exception; + } + + } + + public boolean isDeclaredException(Method m, Class clazz) { + for (Class cl : m.getExceptionTypes()) { + if (cl.equals(clazz) || cl.isAssignableFrom(clazz)) return true; + } + return false; + } + + /** + * no-op for this interceptor. no state is stored. + */ + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + // NOOP + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/Validator.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/Validator.java new file mode 100644 index 0000000..70cef5a --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/Validator.java @@ -0,0 +1,57 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.sql.Connection; + +/** + * Interface to be implemented by custom validator classes. + * + * @author mpassell + */ +public interface Validator { + /** + * Validate a connection and return a boolean to indicate if it's valid. + * + * @param connection the Connection object to test + * @param validateAction the action used. One of {@link PooledConnection#VALIDATE_BORROW}, + * {@link PooledConnection#VALIDATE_IDLE}, {@link PooledConnection#VALIDATE_INIT} or + * {@link PooledConnection#VALIDATE_RETURN} + * @return true if the connection is valid + */ + public boolean validate(Connection connection, int validateAction); +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java new file mode 100644 index 0000000..4bdb198 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java @@ -0,0 +1,57 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomcat.jdbc.pool; + +public class XADataSource extends DataSource implements javax.sql.XADataSource { + + /** + * Constructor for reflection only. A default set of pool properties will be created. + */ + public XADataSource() { + super(); + } + + /** + * Constructs a DataSource object wrapping a connection + * @param poolProperties The pool configuration + */ + public XADataSource(PoolConfiguration poolProperties) { + super(poolProperties); + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java new file mode 100644 index 0000000..e2f1414 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java @@ -0,0 +1,158 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool.interceptor; + +import java.lang.reflect.Method; + +import org.apache.tomcat.jdbc.pool.ConnectionPool; +import org.apache.tomcat.jdbc.pool.JdbcInterceptor; +import org.apache.tomcat.jdbc.pool.PooledConnection; + +/** + * Abstraction interceptor. This component intercepts all calls to create some type of SQL statement. + * By extending this class, one can intercept queries and update statements by overriding the {@link #createStatement(Object, Method, Object[], Object, long)} + * method. + * @version 1.0 + */ +public abstract class AbstractCreateStatementInterceptor extends JdbcInterceptor { + protected static final String CREATE_STATEMENT = "createStatement"; + protected static final int CREATE_STATEMENT_IDX = 0; + protected static final String PREPARE_STATEMENT = "prepareStatement"; + protected static final int PREPARE_STATEMENT_IDX = 1; + protected static final String PREPARE_CALL = "prepareCall"; + protected static final int PREPARE_CALL_IDX = 2; + + protected static final String[] STATEMENT_TYPES = {CREATE_STATEMENT, PREPARE_STATEMENT, PREPARE_CALL}; + protected static final int STATEMENT_TYPE_COUNT = STATEMENT_TYPES.length; + + protected static final String EXECUTE = "execute"; + protected static final String EXECUTE_QUERY = "executeQuery"; + protected static final String EXECUTE_UPDATE = "executeUpdate"; + protected static final String EXECUTE_BATCH = "executeBatch"; + + protected static final String[] EXECUTE_TYPES = {EXECUTE, EXECUTE_QUERY, EXECUTE_UPDATE, EXECUTE_BATCH}; + + public AbstractCreateStatementInterceptor() { + super(); + } + + /** + * {@inheritDoc} + */ + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (compare(CLOSE_VAL,method)) { + closeInvoked(); + return super.invoke(proxy, method, args); + } else { + boolean process = false; + process = isStatement(method, process); + if (process) { + long start = System.currentTimeMillis(); + Object statement = super.invoke(proxy,method,args); + long delta = System.currentTimeMillis() - start; + return createStatement(proxy,method,args,statement, delta); + } else { + return super.invoke(proxy,method,args); + } + } + } + + /** + * This method will be invoked after a successful statement creation. This method can choose to return a wrapper + * around the statement or return the statement itself. + * If this method returns a wrapper then it should return a wrapper object that implements one of the following interfaces. + * {@link java.sql.Statement}, {@link java.sql.PreparedStatement} or {@link java.sql.CallableStatement} + * @param proxy the actual proxy object + * @param method the method that was called. It will be one of the methods defined in {@link #STATEMENT_TYPES} + * @param args the arguments to the method + * @param statement the statement that the underlying connection created + * @param time Elapsed time + * @return a {@link java.sql.Statement} object + */ + public abstract Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time); + + /** + * Method invoked when the operation {@link java.sql.Connection#close()} is invoked. + */ + public abstract void closeInvoked(); + + /** + * Returns true if the method that is being invoked matches one of the statement types. + * + * @param method the method being invoked on the proxy + * @param process boolean result used for recursion + * @return returns true if the method name matched + */ + protected boolean isStatement(Method method, boolean process){ + return process(STATEMENT_TYPES, method, process); + } + + /** + * Returns true if the method that is being invoked matches one of the execute types. + * + * @param method the method being invoked on the proxy + * @param process boolean result used for recursion + * @return returns true if the method name matched + */ + protected boolean isExecute(Method method, boolean process){ + return process(EXECUTE_TYPES, method, process); + } + + /* + * Returns true if the method that is being invoked matches one of the method names passed in + * @param names list of method names that we want to intercept + * @param method the method being invoked on the proxy + * @param process boolean result used for recursion + * @return returns true if the method name matched + */ + protected boolean process(String[] names, Method method, boolean process) { + final String name = method.getName(); + for (int i=0; (!process) && i[] constructors = + new Constructor[AbstractCreateStatementInterceptor.STATEMENT_TYPE_COUNT]; + + + public AbstractQueryReport() { + super(); + } + + /** + * Invoked when prepareStatement has been called and completed. + * @param sql - the string used to prepare the statement with + * @param time - the time it took to invoke prepare + */ + protected abstract void prepareStatement(String sql, long time); + + /** + * Invoked when prepareCall has been called and completed. + * @param query - the string used to prepare the statement with + * @param time - the time it took to invoke prepare + */ + protected abstract void prepareCall(String query, long time); + + /** + * Invoked when a query execution, a call to execute/executeQuery or executeBatch failed. + * @param query the query that was executed and failed + * @param args the arguments to the execution + * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)} + * @param start the time the query execution started + * @param t the exception that happened + * @return - the SQL that was executed or the string "batch" if it was a batch execution + */ + protected String reportFailedQuery(String query, Object[] args, final String name, long start, Throwable t) { + //extract the query string + String sql = (query==null && args!=null && args.length>0)?(String)args[0]:query; + //if we do batch execution, then we name the query 'batch' + if (sql==null && compare(EXECUTE_BATCH,name)) { + sql = "batch"; + } + return sql; + } + + /** + * Invoked when a query execution, a call to execute/executeQuery or executeBatch succeeded and was within the timing threshold + * @param query the query that was executed and failed + * @param args the arguments to the execution + * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)} + * @param start the time the query execution started + * @param delta the time the execution took + * @return - the SQL that was executed or the string "batch" if it was a batch execution + */ + protected String reportQuery(String query, Object[] args, final String name, long start, long delta) { + //extract the query string + String sql = (query==null && args!=null && args.length>0)?(String)args[0]:query; + //if we do batch execution, then we name the query 'batch' + if (sql==null && compare(EXECUTE_BATCH,name)) { + sql = "batch"; + } + return sql; + } + + /** + * Invoked when a query execution, a call to execute/executeQuery or executeBatch succeeded and was exceeded the timing threshold + * @param query the query that was executed and failed + * @param args the arguments to the execution + * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)} + * @param start the time the query execution started + * @param delta the time the execution took + * @return - the SQL that was executed or the string "batch" if it was a batch execution + */ + protected String reportSlowQuery(String query, Object[] args, final String name, long start, long delta) { + //extract the query string + String sql = (query==null && args!=null && args.length>0)?(String)args[0]:query; + //if we do batch execution, then we name the query 'batch' + if (sql==null && compare(EXECUTE_BATCH,name)) { + sql = "batch"; + } + return sql; + } + + /** + * returns the query measure threshold. + * This value is in milliseconds. If the query is faster than this threshold than it wont be accounted for + * @return the threshold in milliseconds + */ + public long getThreshold() { + return threshold; + } + + /** + * Sets the query measurement threshold. The value is in milliseconds. + * If the query goes faster than this threshold it will not be recorded. + * @param threshold set to -1 to record every query. Value is in milliseconds. + */ + public void setThreshold(long threshold) { + this.threshold = threshold; + } + + /** + * Creates a constructor for a proxy class, if one doesn't already exist + * @param idx - the index of the constructor + * @param clazz - the interface that the proxy will implement + * @return - returns a constructor used to create new instances + * @throws NoSuchMethodException Constructor not found + */ + protected Constructor getConstructor(int idx, Class clazz) throws NoSuchMethodException { + if (constructors[idx]==null) { + Class proxyClass = Proxy.getProxyClass(SlowQueryReport.class.getClassLoader(), new Class[] {clazz}); + constructors[idx] = proxyClass.getConstructor(new Class[] { InvocationHandler.class }); + } + return constructors[idx]; + } + + /** + * Creates a statement interceptor to monitor query response times + */ + @Override + public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) { + try { + Object result = null; + String name = method.getName(); + String sql = null; + Constructor constructor = null; + if (compare(CREATE_STATEMENT,name)) { + //createStatement + constructor = getConstructor(CREATE_STATEMENT_IDX,Statement.class); + }else if (compare(PREPARE_STATEMENT,name)) { + //prepareStatement + sql = (String)args[0]; + constructor = getConstructor(PREPARE_STATEMENT_IDX,PreparedStatement.class); + if (sql!=null) { + prepareStatement(sql, time); + } + }else if (compare(PREPARE_CALL,name)) { + //prepareCall + sql = (String)args[0]; + constructor = getConstructor(PREPARE_CALL_IDX,CallableStatement.class); + prepareCall(sql,time); + }else { + //do nothing, might be a future unsupported method + //so we better bail out and let the system continue + return statement; + } + result = constructor.newInstance(new Object[] { new StatementProxy(statement,sql) }); + return result; + }catch (Exception x) { + log.warn("Unable to create statement proxy for slow query report.",x); + } + return statement; + } + + + /** + * Class to measure query execute time + * + */ + protected class StatementProxy implements InvocationHandler { + protected boolean closed = false; + protected Object delegate; + protected final String query; + public StatementProxy(Object parent, String query) { + this.delegate = parent; + this.query = query; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + //get the name of the method for comparison + final String name = method.getName(); + //was close invoked? + boolean close = compare(JdbcInterceptor.CLOSE_VAL,name); + //allow close to be called multiple times + if (close && closed) return null; + //are we calling isClosed? + if (compare(JdbcInterceptor.ISCLOSED_VAL,name)) return Boolean.valueOf(closed); + //if we are calling anything else, bail out + if (closed) throw new SQLException("Statement closed."); + boolean process = false; + //check to see if we are about to execute a query + process = isExecute( method, process); + //if we are executing, get the current time + long start = (process)?System.currentTimeMillis():0; + Object result = null; + try { + //execute the query + result = method.invoke(delegate,args); + }catch (Throwable t) { + reportFailedQuery(query,args,name,start,t); + if (t instanceof InvocationTargetException + && t.getCause() != null) { + throw t.getCause(); + } else { + throw t; + } + } + //measure the time + long delta = (process)?(System.currentTimeMillis()-start):Long.MIN_VALUE; + //see if we meet the requirements to measure + if (delta>threshold) { + try { + //report the slow query + reportSlowQuery(query, args, name, start, delta); + }catch (Exception t) { + if (log.isWarnEnabled()) log.warn("Unable to process slow query",t); + } + } else if (process) { + reportQuery(query, args, name, start, delta); + } + //perform close cleanup + if (close) { + closed=true; + delegate = null; + } + return result; + } + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java new file mode 100644 index 0000000..95049c6 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java @@ -0,0 +1,184 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool.interceptor; + +import java.lang.reflect.Method; +import java.sql.SQLException; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jdbc.pool.ConnectionPool; +import org.apache.tomcat.jdbc.pool.DataSourceFactory; +import org.apache.tomcat.jdbc.pool.JdbcInterceptor; +import org.apache.tomcat.jdbc.pool.PoolConfiguration; +import org.apache.tomcat.jdbc.pool.PooledConnection; + +/** + * Interceptor that keep track of connection state to avoid roundtrips to the database. + * The {@link org.apache.tomcat.jdbc.pool.ConnectionPool} is optimized to do as little work as possible. + * The pool itself doesn't remember settings like {@link java.sql.Connection#setAutoCommit(boolean)}, + * {@link java.sql.Connection#setReadOnly(boolean)}, {@link java.sql.Connection#setCatalog(String)} or + * {@link java.sql.Connection#setTransactionIsolation(int)}. It relies on the application to remember how and when + * these settings have been applied. + * In the cases where the application code doesn't know or want to keep track of the state, this interceptor helps cache the + * state, and it also avoids roundtrips to the database asking for it. + * + */ + +public class ConnectionState extends JdbcInterceptor { + private static final Log log = LogFactory.getLog(ConnectionState.class); + + protected final String[] readState = {"getAutoCommit","getTransactionIsolation","isReadOnly","getCatalog"}; + protected final String[] writeState = {"setAutoCommit","setTransactionIsolation","setReadOnly","setCatalog"}; + + protected Boolean autoCommit = null; + protected Integer transactionIsolation = null; + protected Boolean readOnly = null; + protected String catalog = null; + + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + if (parent==null || con==null) { + //we are resetting, reset our defaults + autoCommit = null; + transactionIsolation = null; + readOnly = null; + catalog = null; + return; + } + PoolConfiguration poolProperties = parent.getPoolProperties(); + if (poolProperties.getDefaultTransactionIsolation()!=DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION) { + try { + if (transactionIsolation==null || transactionIsolation.intValue()!=poolProperties.getDefaultTransactionIsolation()) { + con.getConnection().setTransactionIsolation(poolProperties.getDefaultTransactionIsolation()); + transactionIsolation = Integer.valueOf(poolProperties.getDefaultTransactionIsolation()); + } + }catch (SQLException x) { + transactionIsolation = null; + log.error("Unable to reset transaction isolation state to connection.",x); + } + } + if (poolProperties.getDefaultReadOnly()!=null) { + try { + if (readOnly==null || readOnly.booleanValue()!=poolProperties.getDefaultReadOnly().booleanValue()) { + con.getConnection().setReadOnly(poolProperties.getDefaultReadOnly().booleanValue()); + readOnly = poolProperties.getDefaultReadOnly(); + } + }catch (SQLException x) { + readOnly = null; + log.error("Unable to reset readonly state to connection.",x); + } + } + if (poolProperties.getDefaultAutoCommit()!=null) { + try { + if (autoCommit==null || autoCommit.booleanValue()!=poolProperties.getDefaultAutoCommit().booleanValue()) { + con.getConnection().setAutoCommit(poolProperties.getDefaultAutoCommit().booleanValue()); + autoCommit = poolProperties.getDefaultAutoCommit(); + } + }catch (SQLException x) { + autoCommit = null; + log.error("Unable to reset autocommit state to connection.",x); + } + } + if (poolProperties.getDefaultCatalog()!=null) { + try { + if (catalog==null || (!catalog.equals(poolProperties.getDefaultCatalog()))) { + con.getConnection().setCatalog(poolProperties.getDefaultCatalog()); + catalog = poolProperties.getDefaultCatalog(); + } + }catch (SQLException x) { + catalog = null; + log.error("Unable to reset default catalog state to connection.",x); + } + } + + } + + + @Override + public void disconnected(ConnectionPool parent, PooledConnection con, boolean finalizing) { + //we are resetting, reset our defaults + autoCommit = null; + transactionIsolation = null; + readOnly = null; + catalog = null; + super.disconnected(parent, con, finalizing); + } + + + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String name = method.getName(); + boolean read = false; + int index = -1; + for (int i=0; (!read) && i properties) { + super.setProperties(properties); + InterceptorProperty p = properties.get("queryTimeout"); + if (p!=null) timeout = p.getValueAsInt(timeout); + } + + @Override + public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) { + if (statement instanceof Statement && timeout > 0) { + Statement s = (Statement)statement; + try { + s.setQueryTimeout(timeout); + }catch (SQLException x) { + log.warn("[QueryTimeoutInterceptor] Unable to set query timeout:"+x.getMessage(),x); + } + } + return statement; + } + + @Override + public void closeInvoked() { + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ResetAbandonedTimer.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ResetAbandonedTimer.java new file mode 100644 index 0000000..6c6981e --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/ResetAbandonedTimer.java @@ -0,0 +1,112 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomcat.jdbc.pool.interceptor; + +import java.lang.reflect.Method; + +import org.apache.tomcat.jdbc.pool.JdbcInterceptor; +import org.apache.tomcat.jdbc.pool.PooledConnection; +import org.apache.tomcat.jdbc.pool.ProxyConnection; + +/** + * Class that resets the abandoned timer on any activity on the + * Connection or any successful query executions. + * This interceptor is useful for when you have a {@link org.apache.tomcat.jdbc.pool.PoolConfiguration#setRemoveAbandonedTimeout(int)} + * that is fairly low, and you want to reset the abandoned time each time any operation on the connection is performed + * This is useful for batch processing programs that use connections for extensive amount of times. + * + */ +public class ResetAbandonedTimer extends AbstractQueryReport { + + public ResetAbandonedTimer() { + } + + public boolean resetTimer() { + boolean result = false; + JdbcInterceptor interceptor = this.getNext(); + while (interceptor!=null && result==false) { + if (interceptor instanceof ProxyConnection) { + PooledConnection con = ((ProxyConnection)interceptor).getConnection(); + if (con!=null) { + con.setTimestamp(System.currentTimeMillis()); + result = true; + } else { + break; + } + } + interceptor = interceptor.getNext(); + } + return result; + } + + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Object result = super.invoke(proxy, method, args); + resetTimer(); + return result; + } + + @Override + protected void prepareCall(String query, long time) { + resetTimer(); + } + + @Override + protected void prepareStatement(String sql, long time) { + resetTimer(); + + } + + @Override + public void closeInvoked() { + resetTimer(); + } + + @Override + protected String reportQuery(String query, Object[] args, String name,long start, long delta) { + resetTimer(); + return super.reportQuery(query, args, name, start, delta); + } + + @Override + protected String reportSlowQuery(String query, Object[] args, String name,long start, long delta) { + resetTimer(); + return super.reportSlowQuery(query, args, name, start, delta); + } +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java new file mode 100644 index 0000000..23fa3fa --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java @@ -0,0 +1,516 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool.interceptor; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; +import java.util.concurrent.ConcurrentHashMap; + +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jdbc.pool.ConnectionPool; +import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty; +import org.apache.tomcat.jdbc.pool.PooledConnection; + +/** + * Slow query report interceptor. Tracks timing of query executions. + * @version 1.0 + */ +public class SlowQueryReport extends AbstractQueryReport { + //logger + private static final Log log = LogFactory.getLog(SlowQueryReport.class); + + /** + * we will be keeping track of query stats on a per pool basis + */ + protected static final ConcurrentHashMap> perPoolStats = + new ConcurrentHashMap<>(); + /** + * the queries that are used for this interceptor. + */ + protected volatile ConcurrentHashMap queries = null; + /** + * Maximum number of queries we will be storing + */ + protected int maxQueries= 1000; //don't store more than this amount of queries + + /** + * Flag to enable disable logging of slow queries + */ + protected boolean logSlow = true; + + /** + * Flag to enable disable logging of failed queries + */ + protected boolean logFailed = false; + + /** + * Sort QueryStats by last invocation time + */ + protected final Comparator queryStatsComparator = new QueryStatsComparator(); + + /** + * Returns the query stats for a given pool + * @param poolname - the name of the pool we want to retrieve stats for + * @return a hash map containing statistics for 0 to maxQueries + */ + public static ConcurrentHashMap getPoolStats(String poolname) { + return perPoolStats.get(poolname); + } + + /** + * Creates a slow query report interceptor + */ + public SlowQueryReport() { + super(); + } + + public void setMaxQueries(int maxQueries) { + this.maxQueries = maxQueries; + } + + + @Override + protected String reportFailedQuery(String query, Object[] args, String name, long start, Throwable t) { + String sql = super.reportFailedQuery(query, args, name, start, t); + if (this.maxQueries > 0 ) { + long now = System.currentTimeMillis(); + long delta = now - start; + QueryStats qs = this.getQueryStats(sql); + if (qs != null) { + qs.failure(delta, now); + if (isLogFailed() && log.isWarnEnabled()) { + log.warn("Failed Query Report SQL="+sql+"; time="+delta+" ms;"); + } + } + } + return sql; + } + + @Override + protected String reportQuery(String query, Object[] args, final String name, long start, long delta) { + String sql = super.reportQuery(query, args, name, start, delta); + if (this.maxQueries > 0 ) { + QueryStats qs = this.getQueryStats(sql); + if (qs != null) qs.add(delta, start); + } + return sql; + } + + @Override + protected String reportSlowQuery(String query, Object[] args, String name, long start, long delta) { + String sql = super.reportSlowQuery(query, args, name, start, delta); + if (this.maxQueries > 0 ) { + QueryStats qs = this.getQueryStats(sql); + if (qs != null) { + qs.add(delta, start); + if (isLogSlow() && log.isWarnEnabled()) { + log.warn("Slow Query Report SQL="+sql+"; time="+delta+" ms;"); + } + } + } + return sql; + } + + /** + * invoked when the connection receives the close request + * Not used for now. + */ + @Override + public void closeInvoked() { + // NOOP + } + + @Override + public void prepareStatement(String sql, long time) { + if (this.maxQueries > 0 ) { + QueryStats qs = getQueryStats(sql); + if (qs != null) qs.prepare(time); + } + } + + @Override + public void prepareCall(String sql, long time) { + if (this.maxQueries > 0 ) { + QueryStats qs = getQueryStats(sql); + if (qs != null) qs.prepare(time); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void poolStarted(ConnectionPool pool) { + super.poolStarted(pool); + //see if we already created a map for this pool + queries = SlowQueryReport.perPoolStats.get(pool.getName()); + if (queries==null) { + //create the map to hold our stats + //however TODO we need to improve the eviction + //selection + queries = new ConcurrentHashMap<>(); + if (perPoolStats.putIfAbsent(pool.getName(), queries)!=null) { + //there already was one + queries = SlowQueryReport.perPoolStats.get(pool.getName()); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void poolClosed(ConnectionPool pool) { + perPoolStats.remove(pool.getName()); + super.poolClosed(pool); + } + + protected QueryStats getQueryStats(String sql) { + if (sql==null) sql = ""; + ConcurrentHashMap queries = SlowQueryReport.this.queries; + if (queries==null) { + if (log.isWarnEnabled()) log.warn("Connection has already been closed or abandoned"); + return null; + } + QueryStats qs = queries.get(sql); + if (qs == null) { + qs = new QueryStats(sql); + if (queries.putIfAbsent(sql,qs)!=null) { + qs = queries.get(sql); + } else { + //we added a new element, see if we need to remove the oldest + if (queries.size() > maxQueries) { + removeOldest(queries); + } + } + } + return qs; + } + + /** + * Sort QueryStats by last invocation time + * @param queries The queries map + */ + protected void removeOldest(ConcurrentHashMap queries) { + ArrayList list = new ArrayList<>(queries.values()); + Collections.sort(list, queryStatsComparator); + int removeIndex = 0; + while (queries.size() > maxQueries) { + String sql = list.get(removeIndex).getQuery(); + queries.remove(sql); + if (log.isDebugEnabled()) log.debug("Removing slow query, capacity reached:"+sql); + removeIndex++; + } + } + + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + super.reset(parent, con); + if (parent!=null) + queries = SlowQueryReport.perPoolStats.get(parent.getName()); + else + queries = null; + } + + + public boolean isLogSlow() { + return logSlow; + } + + public void setLogSlow(boolean logSlow) { + this.logSlow = logSlow; + } + + public boolean isLogFailed() { + return logFailed; + } + + public void setLogFailed(boolean logFailed) { + this.logFailed = logFailed; + } + + @Override + public void setProperties(Map properties) { + super.setProperties(properties); + final String threshold = "threshold"; + final String maxqueries= "maxQueries"; + final String logslow = "logSlow"; + final String logfailed = "logFailed"; + InterceptorProperty p1 = properties.get(threshold); + InterceptorProperty p2 = properties.get(maxqueries); + InterceptorProperty p3 = properties.get(logslow); + InterceptorProperty p4 = properties.get(logfailed); + if (p1!=null) { + setThreshold(Long.parseLong(p1.getValue())); + } + if (p2!=null) { + setMaxQueries(Integer.parseInt(p2.getValue())); + } + if (p3!=null) { + setLogSlow(Boolean.parseBoolean(p3.getValue())); + } + if (p4!=null) { + setLogFailed(Boolean.parseBoolean(p4.getValue())); + } + } + + + public static class QueryStats { + static final String[] FIELD_NAMES = new String[] { + "query", + "nrOfInvocations", + "maxInvocationTime", + "maxInvocationDate", + "minInvocationTime", + "minInvocationDate", + "totalInvocationTime", + "failures", + "prepareCount", + "prepareTime", + "lastInvocation" + }; + + static final String[] FIELD_DESCRIPTIONS = new String[] { + "The SQL query", + "The number of query invocations, a call to executeXXX", + "The longest time for this query in milliseconds", + "The time and date for when the longest query took place", + "The shortest time for this query in milliseconds", + "The time and date for when the shortest query took place", + "The total amount of milliseconds spent executing this query", + "The number of failures for this query", + "The number of times this query was prepared (prepareStatement/prepareCall)", + "The total number of milliseconds spent preparing this query", + "The date and time of the last invocation" + }; + + static final OpenType[] FIELD_TYPES = new OpenType[] { + SimpleType.STRING, + SimpleType.INTEGER, + SimpleType.LONG, + SimpleType.LONG, + SimpleType.LONG, + SimpleType.LONG, + SimpleType.LONG, + SimpleType.LONG, + SimpleType.INTEGER, + SimpleType.LONG, + SimpleType.LONG + }; + + private final String query; + private volatile int nrOfInvocations; + private volatile long maxInvocationTime = Long.MIN_VALUE; + private volatile long maxInvocationDate; + private volatile long minInvocationTime = Long.MAX_VALUE; + private volatile long minInvocationDate; + private volatile long totalInvocationTime; + private volatile long failures; + private volatile int prepareCount; + private volatile long prepareTime; + private volatile long lastInvocation = 0; + + public static String[] getFieldNames() { + return FIELD_NAMES; + } + + public static String[] getFieldDescriptions() { + return FIELD_DESCRIPTIONS; + } + + public static OpenType[] getFieldTypes() { + return FIELD_TYPES; + } + + @Override + public String toString() { + SimpleDateFormat sdf = + new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US); + sdf.setTimeZone(TimeZone.getTimeZone("GMT")); + StringBuilder buf = new StringBuilder("QueryStats[query:"); + buf.append(query); + buf.append(", nrOfInvocations:"); + buf.append(nrOfInvocations); + buf.append(", maxInvocationTime:"); + buf.append(maxInvocationTime); + buf.append(", maxInvocationDate:"); + buf.append(sdf.format(new java.util.Date(maxInvocationDate))); + buf.append(", minInvocationTime:"); + buf.append(minInvocationTime); + buf.append(", minInvocationDate:"); + buf.append(sdf.format(new java.util.Date(minInvocationDate))); + buf.append(", totalInvocationTime:"); + buf.append(totalInvocationTime); + buf.append(", averageInvocationTime:"); + buf.append((float)totalInvocationTime / (float)nrOfInvocations); + buf.append(", failures:"); + buf.append(failures); + buf.append(", prepareCount:"); + buf.append(prepareCount); + buf.append(", prepareTime:"); + buf.append(prepareTime); + buf.append("]"); + return buf.toString(); + } + + public CompositeDataSupport getCompositeData(final CompositeType type) throws OpenDataException{ + Object[] values = new Object[] { + query, + Integer.valueOf(nrOfInvocations), + Long.valueOf(maxInvocationTime), + Long.valueOf(maxInvocationDate), + Long.valueOf(minInvocationTime), + Long.valueOf(minInvocationDate), + Long.valueOf(totalInvocationTime), + Long.valueOf(failures), + Integer.valueOf(prepareCount), + Long.valueOf(prepareTime), + Long.valueOf(lastInvocation) + }; + return new CompositeDataSupport(type,FIELD_NAMES,values); + } + + public QueryStats(String query) { + this.query = query; + } + + public void prepare(long invocationTime) { + prepareCount++; + prepareTime+=invocationTime; + + } + + public void add(long invocationTime, long now) { + //not thread safe, but don't sacrifice performance for this kind of stuff + maxInvocationTime = Math.max(invocationTime, maxInvocationTime); + if (maxInvocationTime == invocationTime) { + maxInvocationDate = now; + } + minInvocationTime = Math.min(invocationTime, minInvocationTime); + if (minInvocationTime==invocationTime) { + minInvocationDate = now; + } + nrOfInvocations++; + totalInvocationTime+=invocationTime; + lastInvocation = now; + } + + public void failure(long invocationTime, long now) { + add(invocationTime,now); + failures++; + + } + + public String getQuery() { + return query; + } + + public int getNrOfInvocations() { + return nrOfInvocations; + } + + public long getMaxInvocationTime() { + return maxInvocationTime; + } + + public long getMaxInvocationDate() { + return maxInvocationDate; + } + + public long getMinInvocationTime() { + return minInvocationTime; + } + + public long getMinInvocationDate() { + return minInvocationDate; + } + + public long getTotalInvocationTime() { + return totalInvocationTime; + } + + @Override + public int hashCode() { + return query.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof QueryStats) { + QueryStats qs = (QueryStats)other; + return qs.query.equals(this.query); + } + return false; + } + + public boolean isOlderThan(QueryStats other) { + return this.lastInvocation < other.lastInvocation; + } + } + + /** Compare QueryStats by their lastInvocation value. QueryStats that + * have never been updated, have a lastInvocation value of {@code 0} + * which should be handled as the newest possible invocation. + */ + private static class QueryStatsComparator implements Comparator { + + @Override + public int compare(QueryStats stats1, QueryStats stats2) { + return Long.compare(handleZero(stats1.lastInvocation), + handleZero(stats2.lastInvocation)); + } + + private static long handleZero(long value) { + return value == 0 ? Long.MAX_VALUE : value; + } + + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java new file mode 100644 index 0000000..60b48a0 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java @@ -0,0 +1,338 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool.interceptor; + +import java.lang.management.ManagementFactory; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanRegistrationException; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.RuntimeOperationsException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jdbc.pool.ConnectionPool; +import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty; +import org.apache.tomcat.jdbc.pool.PooledConnection; +/** + * Publishes data to JMX and provides notifications + * when failures happen. + * + */ +public class SlowQueryReportJmx extends SlowQueryReport implements NotificationEmitter, SlowQueryReportJmxMBean{ + public static final String SLOW_QUERY_NOTIFICATION = "SLOW QUERY"; + public static final String FAILED_QUERY_NOTIFICATION = "FAILED QUERY"; + + public static final String objectNameAttribute = "objectName"; + + protected static volatile CompositeType SLOW_QUERY_TYPE; + + private static final Log log = LogFactory.getLog(SlowQueryReportJmx.class); + + + protected static final ConcurrentHashMap mbeans = + new ConcurrentHashMap<>(); + + + //==============================JMX STUFF======================== + protected volatile NotificationBroadcasterSupport notifier = new NotificationBroadcasterSupport(); + + @Override + public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException { + notifier.addNotificationListener(listener, filter, handback); + } + + + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + return notifier.getNotificationInfo(); + } + + @Override + public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { + notifier.removeNotificationListener(listener); + + } + + @Override + public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { + notifier.removeNotificationListener(listener, filter, handback); + + } + + + //==============================JMX STUFF======================== + + protected String poolName = null; + + protected static final AtomicLong notifySequence = new AtomicLong(0); + + protected boolean notifyPool = true; + + protected ConnectionPool pool = null; + + protected static CompositeType getCompositeType() { + if (SLOW_QUERY_TYPE==null) { + try { + SLOW_QUERY_TYPE = new CompositeType( + SlowQueryReportJmx.class.getName(), + "Composite data type for query statistics", + QueryStats.getFieldNames(), + QueryStats.getFieldDescriptions(), + QueryStats.getFieldTypes()); + }catch (OpenDataException x) { + log.warn("Unable to initialize composite data type for JMX stats and notifications.",x); + } + } + return SLOW_QUERY_TYPE; + } + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + super.reset(parent, con); + if (parent!=null) { + poolName = parent.getName(); + pool = parent; + registerJmx(); + } + } + + + @Override + public void poolClosed(ConnectionPool pool) { + this.poolName = pool.getName(); + deregisterJmx(); + super.poolClosed(pool); + } + + @Override + public void poolStarted(ConnectionPool pool) { + this.pool = pool; + super.poolStarted(pool); + this.poolName = pool.getName(); + } + + @Override + protected String reportFailedQuery(String query, Object[] args, String name, long start, Throwable t) { + query = super.reportFailedQuery(query, args, name, start, t); + if (isLogFailed()) notifyJmx(query,FAILED_QUERY_NOTIFICATION); + return query; + } + + protected void notifyJmx(String query, String type) { + try { + long sequence = notifySequence.incrementAndGet(); + + if (isNotifyPool()) { + if (this.pool!=null && this.pool.getJmxPool()!=null) { + this.pool.getJmxPool().notify(type, query); + } + } else { + if (notifier!=null) { + Notification notification = + new Notification(type, + this, + sequence, + System.currentTimeMillis(), + query); + + notifier.sendNotification(notification); + } + } + } catch (RuntimeOperationsException e) { + if (log.isDebugEnabled()) { + log.debug("Unable to send failed query notification.",e); + } + } + } + + @Override + protected String reportSlowQuery(String query, Object[] args, String name, long start, long delta) { + query = super.reportSlowQuery(query, args, name, start, delta); + if (isLogSlow()) notifyJmx(query,SLOW_QUERY_NOTIFICATION); + return query; + } + + /** + * JMX operation - return the names of all the pools + * @return - all the names of pools that we have stored data for + */ + public String[] getPoolNames() { + Set keys = perPoolStats.keySet(); + return keys.toArray(new String[0]); + } + + /** + * JMX operation - return the name of the pool + * @return the name of the pool, unique within the JVM + */ + public String getPoolName() { + return poolName; + } + + + public boolean isNotifyPool() { + return notifyPool; + } + + public void setNotifyPool(boolean notifyPool) { + this.notifyPool = notifyPool; + } + + /** + * JMX operation - remove all stats for this connection pool + */ + public void resetStats() { + ConcurrentHashMap queries = perPoolStats.get(poolName); + if (queries!=null) { + Iterator it = queries.keySet().iterator(); + while (it.hasNext()) it.remove(); + } + } + + /** + * JMX operation - returns all the queries we have collected. + * @return - the slow query report as composite data. + */ + @Override + public CompositeData[] getSlowQueriesCD() throws OpenDataException { + CompositeDataSupport[] result = null; + ConcurrentHashMap queries = perPoolStats.get(poolName); + if (queries!=null) { + Set> stats = queries.entrySet(); + if (stats!=null) { + result = new CompositeDataSupport[stats.size()]; + Iterator> it = stats.iterator(); + int pos = 0; + while (it.hasNext()) { + Map.Entry entry = it.next(); + QueryStats qs = entry.getValue(); + result[pos++] = qs.getCompositeData(getCompositeType()); + } + } + } + return result; + } + + protected void deregisterJmx() { + try { + if (mbeans.remove(poolName)!=null) { + ObjectName oname = getObjectName(getClass(),poolName); + ManagementFactory.getPlatformMBeanServer().unregisterMBean(oname); + } + } catch (MBeanRegistrationException e) { + log.debug("Jmx deregistration failed.",e); + } catch (InstanceNotFoundException e) { + log.debug("Jmx deregistration failed.",e); + } catch (MalformedObjectNameException e) { + log.warn("Jmx deregistration failed.",e); + } catch (RuntimeOperationsException e) { + log.warn("Jmx deregistration failed.",e); + } + + } + + + public ObjectName getObjectName(Class clazz, String poolName) throws MalformedObjectNameException { + ObjectName oname; + Map properties = getProperties(); + if (properties != null && properties.containsKey(objectNameAttribute)) { + oname = new ObjectName(properties.get(objectNameAttribute).getValue()); + } else { + oname = new ObjectName(ConnectionPool.POOL_JMX_TYPE_PREFIX+clazz.getName()+",name=" + poolName); + } + return oname; + } + + protected void registerJmx() { + try { + //only if we notify the pool itself + if (isNotifyPool()) { + + } else if (getCompositeType()!=null) { + ObjectName oname = getObjectName(getClass(),poolName); + if (mbeans.putIfAbsent(poolName, this)==null) { + ManagementFactory.getPlatformMBeanServer().registerMBean(this, oname); + } + } else { + log.warn(SlowQueryReport.class.getName()+ "- No JMX support, composite type was not found."); + } + } catch (MalformedObjectNameException e) { + log.error("Jmx registration failed, no JMX data will be exposed for the query stats.",e); + } catch (RuntimeOperationsException e) { + log.error("Jmx registration failed, no JMX data will be exposed for the query stats.",e); + } catch (MBeanException e) { + log.error("Jmx registration failed, no JMX data will be exposed for the query stats.",e); + } catch (InstanceAlreadyExistsException e) { + log.error("Jmx registration failed, no JMX data will be exposed for the query stats.",e); + } catch (NotCompliantMBeanException e) { + log.error("Jmx registration failed, no JMX data will be exposed for the query stats.",e); + } + } + + @Override + public void setProperties(Map properties) { + super.setProperties(properties); + final String threshold = "notifyPool"; + InterceptorProperty p1 = properties.get(threshold); + if (p1!=null) { + this.setNotifyPool(Boolean.parseBoolean(p1.getValue())); + } + } + + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmxMBean.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmxMBean.java new file mode 100644 index 0000000..202d06e --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmxMBean.java @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool.interceptor; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.OpenDataException; + +public interface SlowQueryReportJmxMBean { + public CompositeData[] getSlowQueriesCD() throws OpenDataException; +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java new file mode 100644 index 0000000..e98d3b6 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java @@ -0,0 +1,358 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool.interceptor; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.tomcat.jdbc.pool.ConnectionPool; +import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty; +import org.apache.tomcat.jdbc.pool.PooledConnection; + +/** + * Interceptor that caches {@code PreparedStatement} and/or + * {@code CallableStatement} instances on a connection. + */ +public class StatementCache extends StatementDecoratorInterceptor { + protected static final String[] ALL_TYPES = new String[] {PREPARE_STATEMENT,PREPARE_CALL}; + protected static final String[] CALLABLE_TYPE = new String[] {PREPARE_CALL}; + protected static final String[] PREPARED_TYPE = new String[] {PREPARE_STATEMENT}; + protected static final String[] NO_TYPE = new String[] {}; + + protected static final String STATEMENT_CACHE_ATTR = StatementCache.class.getName() + ".cache"; + + /*begin properties for the statement cache*/ + private boolean cachePrepared = true; + private boolean cacheCallable = false; + private int maxCacheSize = 50; + private PooledConnection pcon; + private String[] types; + + + public boolean isCachePrepared() { + return cachePrepared; + } + + public boolean isCacheCallable() { + return cacheCallable; + } + + public int getMaxCacheSize() { + return maxCacheSize; + } + + public String[] getTypes() { + return types; + } + + public AtomicInteger getCacheSize() { + return cacheSize; + } + + @Override + public void setProperties(Map properties) { + super.setProperties(properties); + InterceptorProperty p = properties.get("prepared"); + if (p!=null) cachePrepared = p.getValueAsBoolean(cachePrepared); + p = properties.get("callable"); + if (p!=null) cacheCallable = p.getValueAsBoolean(cacheCallable); + p = properties.get("max"); + if (p!=null) maxCacheSize = p.getValueAsInt(maxCacheSize); + if (cachePrepared && cacheCallable) { + this.types = ALL_TYPES; + } else if (cachePrepared) { + this.types = PREPARED_TYPE; + } else if (cacheCallable) { + this.types = CALLABLE_TYPE; + } else { + this.types = NO_TYPE; + } + + } + /*end properties for the statement cache*/ + + /*begin the cache size*/ + private static ConcurrentHashMap cacheSizeMap = + new ConcurrentHashMap<>(); + + private AtomicInteger cacheSize; + + @Override + public void poolStarted(ConnectionPool pool) { + cacheSizeMap.putIfAbsent(pool, new AtomicInteger(0)); + super.poolStarted(pool); + } + + @Override + public void poolClosed(ConnectionPool pool) { + cacheSizeMap.remove(pool); + super.poolClosed(pool); + } + /*end the cache size*/ + + /*begin the actual statement cache*/ + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + super.reset(parent, con); + if (parent==null) { + cacheSize = null; + this.pcon = null; + } else { + cacheSize = cacheSizeMap.get(parent); + this.pcon = con; + if (!pcon.getAttributes().containsKey(STATEMENT_CACHE_ATTR)) { + ConcurrentHashMap cache = + new ConcurrentHashMap<>(); + pcon.getAttributes().put(STATEMENT_CACHE_ATTR,cache); + } + } + } + + @Override + public void disconnected(ConnectionPool parent, PooledConnection con, boolean finalizing) { + @SuppressWarnings("unchecked") + ConcurrentHashMap statements = + (ConcurrentHashMap)con.getAttributes().get(STATEMENT_CACHE_ATTR); + + if (statements!=null) { + for (Map.Entry p : statements.entrySet()) { + closeStatement(p.getValue()); + } + statements.clear(); + } + + super.disconnected(parent, con, finalizing); + } + + public void closeStatement(CachedStatement st) { + if (st==null) return; + st.forceClose(); + } + + @Override + protected Object createDecorator(Object proxy, Method method, Object[] args, + Object statement, Constructor constructor, String sql) + throws InstantiationException, IllegalAccessException, InvocationTargetException { + boolean process = process(this.types, method, false); + if (process) { + Object result = null; + CachedStatement statementProxy = new CachedStatement((Statement)statement,sql); + result = constructor.newInstance(new Object[] { statementProxy }); + statementProxy.setActualProxy(result); + statementProxy.setConnection(proxy); + statementProxy.setConstructor(constructor); + statementProxy.setCacheKey(createCacheKey(method, args)); + return result; + } else { + return super.createDecorator(proxy, method, args, statement, constructor, sql); + } + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + boolean process = process(this.types, method, false); + if (process && args.length>0 && args[0] instanceof String) { + CachedStatement statement = isCached(method, args); + if (statement!=null) { + //remove it from the cache since it is used + removeStatement(statement); + return statement.getActualProxy(); + } else { + return super.invoke(proxy, method, args); + } + } else { + return super.invoke(proxy,method,args); + } + } + + public CachedStatement isCached(Method method, Object[] args) { + @SuppressWarnings("unchecked") + ConcurrentHashMap cache = + (ConcurrentHashMap)pcon.getAttributes().get(STATEMENT_CACHE_ATTR); + return cache.get(createCacheKey(method, args)); + } + + public boolean cacheStatement(CachedStatement proxy) { + @SuppressWarnings("unchecked") + ConcurrentHashMap cache = + (ConcurrentHashMap)pcon.getAttributes().get(STATEMENT_CACHE_ATTR); + if (proxy.getCacheKey()==null) { + return false; + } else if (cache.containsKey(proxy.getCacheKey())) { + return false; + } else if (cacheSize.get()>=maxCacheSize) { + return false; + } else if (cacheSize.incrementAndGet()>maxCacheSize) { + cacheSize.decrementAndGet(); + return false; + } else { + //cache the statement + cache.put(proxy.getCacheKey(), proxy); + return true; + } + } + + public boolean removeStatement(CachedStatement proxy) { + @SuppressWarnings("unchecked") + ConcurrentHashMap cache = + (ConcurrentHashMap)pcon.getAttributes().get(STATEMENT_CACHE_ATTR); + if (cache.remove(proxy.getCacheKey()) != null) { + cacheSize.decrementAndGet(); + return true; + } else { + return false; + } + } + /*end the actual statement cache*/ + + + protected class CachedStatement extends StatementDecoratorInterceptor.StatementProxy { + boolean cached = false; + CacheKey key; + public CachedStatement(Statement parent, String sql) { + super(parent, sql); + } + + @Override + public void closeInvoked() { + //should we cache it + boolean shouldClose = true; + if (cacheSize.get() < maxCacheSize) { + //cache a proxy so that we don't reuse the facade + CachedStatement proxy = new CachedStatement(getDelegate(),getSql()); + proxy.setCacheKey(getCacheKey()); + try { + // clear Resultset + ResultSet result = getDelegate().getResultSet(); + if (result != null && !result.isClosed()) { + result.close(); + } + //create a new facade + Object actualProxy = getConstructor().newInstance(new Object[] { proxy }); + proxy.setActualProxy(actualProxy); + proxy.setConnection(getConnection()); + proxy.setConstructor(getConstructor()); + if (cacheStatement(proxy)) { + proxy.cached = true; + shouldClose = false; + } + } catch (Exception x) { + removeStatement(proxy); + } + } + if (shouldClose) { + super.closeInvoked(); + } + closed = true; + delegate = null; + + } + + public void forceClose() { + removeStatement(this); + super.closeInvoked(); + } + + public CacheKey getCacheKey() { + return key; + } + + public void setCacheKey(CacheKey cacheKey) { + key = cacheKey; + } + + } + + protected CacheKey createCacheKey(Method method, Object[] args) { + return createCacheKey(method.getName(), args); + } + + protected CacheKey createCacheKey(String methodName, Object[] args) { + CacheKey key = null; + if (compare(PREPARE_STATEMENT, methodName)) { + key = new CacheKey(PREPARE_STATEMENT, args); + } else if (compare(PREPARE_CALL, methodName)) { + key = new CacheKey(PREPARE_CALL, args); + } + return key; + } + + + private static final class CacheKey { + private final String stmtType; + private final Object[] args; + private CacheKey(String type, Object[] methodArgs) { + stmtType = type; + args = methodArgs; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(args); + result = prime * result + + ((stmtType == null) ? 0 : stmtType.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CacheKey other = (CacheKey) obj; + if (!Arrays.equals(args, other.args)) + return false; + if (stmtType == null) { + if (other.stmtType != null) + return false; + } else if (!stmtType.equals(other.stmtType)) + return false; + return true; + } + } +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java new file mode 100644 index 0000000..e3b160b --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java @@ -0,0 +1,342 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.tomcat.jdbc.pool.interceptor; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +/** + * Implementation of JdbcInterceptor that proxies resultSets and statements. + * @author Guillermo Fernandes + */ +public class StatementDecoratorInterceptor extends AbstractCreateStatementInterceptor { + + private static final Log logger = LogFactory.getLog(StatementDecoratorInterceptor.class); + + protected static final String EXECUTE_QUERY = "executeQuery"; + protected static final String GET_GENERATED_KEYS = "getGeneratedKeys"; + protected static final String GET_RESULTSET = "getResultSet"; + + protected static final String[] RESULTSET_TYPES = {EXECUTE_QUERY, GET_GENERATED_KEYS, GET_RESULTSET}; + + /** + * the constructors that are used to create statement proxies + */ + protected static final Constructor[] constructors = new Constructor[AbstractCreateStatementInterceptor.STATEMENT_TYPE_COUNT]; + + /** + * the constructor to create the resultSet proxies + */ + protected static Constructor resultSetConstructor = null; + + @Override + public void closeInvoked() { + // nothing to do + } + + /** + * Creates a constructor for a proxy class, if one doesn't already exist + * + * @param idx + * - the index of the constructor + * @param clazz + * - the interface that the proxy will implement + * @return - returns a constructor used to create new instances + * @throws NoSuchMethodException Constructor not found + */ + protected Constructor getConstructor(int idx, Class clazz) throws NoSuchMethodException { + if (constructors[idx] == null) { + Class proxyClass = Proxy.getProxyClass(StatementDecoratorInterceptor.class.getClassLoader(), + new Class[] { clazz }); + constructors[idx] = proxyClass.getConstructor(new Class[] { InvocationHandler.class }); + } + return constructors[idx]; + } + + protected Constructor getResultSetConstructor() throws NoSuchMethodException { + if (resultSetConstructor == null) { + Class proxyClass = Proxy.getProxyClass(StatementDecoratorInterceptor.class.getClassLoader(), + new Class[] { ResultSet.class }); + resultSetConstructor = proxyClass.getConstructor(new Class[] { InvocationHandler.class }); + } + return resultSetConstructor; + } + + /** + * Creates a statement interceptor to monitor query response times + */ + @Override + public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) { + try { + String name = method.getName(); + Constructor constructor = null; + String sql = null; + if (compare(CREATE_STATEMENT, name)) { + // createStatement + constructor = getConstructor(CREATE_STATEMENT_IDX, Statement.class); + } else if (compare(PREPARE_STATEMENT, name)) { + // prepareStatement + constructor = getConstructor(PREPARE_STATEMENT_IDX, PreparedStatement.class); + sql = (String)args[0]; + } else if (compare(PREPARE_CALL, name)) { + // prepareCall + constructor = getConstructor(PREPARE_CALL_IDX, CallableStatement.class); + sql = (String)args[0]; + } else { + // do nothing, might be a future unsupported method + // so we better bail out and let the system continue + return statement; + } + return createDecorator(proxy, method, args, statement, constructor, sql); + } catch (Exception x) { + if (x instanceof InvocationTargetException) { + Throwable cause = x.getCause(); + if (cause instanceof ThreadDeath) { + throw (ThreadDeath) cause; + } + if (cause instanceof VirtualMachineError) { + throw (VirtualMachineError) cause; + } + } + logger.warn("Unable to create statement proxy for slow query report.", x); + } + return statement; + } + + /** + * Creates a proxy for a Statement. + * + * @param proxy The proxy object on which the method that triggered + * the creation of the statement was called. + * @param method The method that was called on the proxy + * @param args The arguments passed as part of the method call to + * the proxy + * @param statement The statement object that is to be proxied + * @param constructor The constructor for the desired proxy + * @param sql The sql of of the statement + * + * @return A new proxy for the Statement + * @throws InstantiationException Couldn't instantiate object + * @throws IllegalAccessException Inaccessible constructor + * @throws InvocationTargetException Exception thrown from constructor + */ + protected Object createDecorator(Object proxy, Method method, Object[] args, + Object statement, Constructor constructor, String sql) + throws InstantiationException, IllegalAccessException, InvocationTargetException { + Object result = null; + StatementProxy statementProxy = + new StatementProxy<>((Statement)statement,sql); + result = constructor.newInstance(new Object[] { statementProxy }); + statementProxy.setActualProxy(result); + statementProxy.setConnection(proxy); + statementProxy.setConstructor(constructor); + return result; + } + + protected boolean isExecuteQuery(String methodName) { + return EXECUTE_QUERY.equals(methodName); + } + + protected boolean isExecuteQuery(Method method) { + return isExecuteQuery(method.getName()); + } + + protected boolean isResultSet(Method method, boolean process) { + return process(RESULTSET_TYPES, method, process); + } + + /** + * Class to measure query execute time. + */ + protected class StatementProxy implements InvocationHandler { + + protected boolean closed = false; + protected T delegate; + private Object actualProxy; + private Object connection; + private String sql; + private Constructor constructor; + + public StatementProxy(T delegate, String sql) { + this.delegate = delegate; + this.sql = sql; + } + public T getDelegate() { + return this.delegate; + } + + public String getSql() { + return sql; + } + + public void setConnection(Object proxy) { + this.connection = proxy; + } + public Object getConnection() { + return this.connection; + } + + public void setActualProxy(Object proxy){ + this.actualProxy = proxy; + } + public Object getActualProxy() { + return this.actualProxy; + } + + + public Constructor getConstructor() { + return constructor; + } + public void setConstructor(Constructor constructor) { + this.constructor = constructor; + } + public void closeInvoked() { + if (getDelegate()!=null) { + try { + getDelegate().close(); + }catch (SQLException ignore) { + } + } + closed = true; + delegate = null; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (compare(TOSTRING_VAL,method)) { + return toString(); + } + // was close invoked? + boolean close = compare(CLOSE_VAL, method); + // allow close to be called multiple times + if (close && closed) + return null; + // are we calling isClosed? + if (compare(ISCLOSED_VAL, method)) + return Boolean.valueOf(closed); + // if we are calling anything else, bail out + if (closed) + throw new SQLException("Statement closed."); + if (compare(GETCONNECTION_VAL,method)){ + return connection; + } + boolean process = false; + process = isResultSet(method, process); + // check to see if we are about to execute a query + // if we are executing, get the current time + Object result = null; + try { + // perform close cleanup + if (close) { + closeInvoked(); + } else { + // execute the query + result = method.invoke(delegate, args); + } + } catch (Throwable t) { + if (t instanceof InvocationTargetException + && t.getCause() != null) { + throw t.getCause(); + } else { + throw t; + } + } + if (process && result != null) { + Constructor cons = getResultSetConstructor(); + result = cons.newInstance(new Object[]{new ResultSetProxy(actualProxy, result)}); + } + return result; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(StatementProxy.class.getName()); + buf.append("[Proxy="); + buf.append(System.identityHashCode(this)); + buf.append("; Sql="); + buf.append(getSql()); + buf.append("; Delegate="); + buf.append(getDelegate()); + buf.append("; Connection="); + buf.append(getConnection()); + buf.append("]"); + return buf.toString(); + } + } + + protected class ResultSetProxy implements InvocationHandler { + + private Object st; + private Object delegate; + + public ResultSetProxy(Object st, Object delegate) { + this.st = st; + this.delegate = delegate; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("getStatement")) { + return this.st; + } else { + try { + return method.invoke(this.delegate, args); + } catch (Throwable t) { + if (t instanceof InvocationTargetException + && t.getCause() != null) { + throw t.getCause(); + } else { + throw t; + } + } + } + } + } +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementFinalizer.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementFinalizer.java new file mode 100644 index 0000000..6535139 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementFinalizer.java @@ -0,0 +1,136 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool.interceptor; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.sql.Statement; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jdbc.pool.ConnectionPool; +import org.apache.tomcat.jdbc.pool.PoolProperties; +import org.apache.tomcat.jdbc.pool.PooledConnection; + +/** + * Keeps track of statements associated with a connection and invokes close upon {@link java.sql.Connection#close()} + * Useful for applications that dont close the associated statements after being done with a connection. + * + */ +public class StatementFinalizer extends AbstractCreateStatementInterceptor { + private static final Log log = LogFactory.getLog(StatementFinalizer.class); + + protected List statements = new LinkedList<>(); + + private boolean logCreationStack = false; + + @Override + public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) { + try { + if (statement instanceof Statement) + statements.add(new StatementEntry((Statement)statement)); + }catch (ClassCastException x) { + //ignore this one + } + return statement; + } + + @SuppressWarnings("null") // st is not null when used + @Override + public void closeInvoked() { + while (statements.size()>0) { + StatementEntry ws = statements.remove(0); + Statement st = ws.getStatement(); + boolean shallClose = false; + try { + shallClose = st!=null && (!st.isClosed()); + if (shallClose) { + st.close(); + } + } catch (Exception ignore) { + if (log.isDebugEnabled()) { + log.debug("Unable to closed statement upon connection close.",ignore); + } + } finally { + if (logCreationStack && shallClose) { + log.warn("Statement created, but was not closed at:", ws.getAllocationStack()); + } + } + } + } + + @Override + public void setProperties(Map properties) { + super.setProperties(properties); + + PoolProperties.InterceptorProperty logProperty = properties.get("trace"); + if (null != logProperty) { + logCreationStack = logProperty.getValueAsBoolean(logCreationStack); + } + } + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + statements.clear(); + super.reset(parent, con); + } + + protected class StatementEntry { + private WeakReference statement; + private Throwable allocationStack; + + public StatementEntry(Statement statement) { + this.statement = new WeakReference<>(statement); + if (logCreationStack) { + this.allocationStack = new Throwable(); + } + } + + public Statement getStatement() { + return statement.get(); + } + + public Throwable getAllocationStack() { + return allocationStack; + } + } + + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/mbeans-descriptors.xml b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/mbeans-descriptors.xml new file mode 100644 index 0000000..0cca7ad --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/mbeans-descriptors.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + Slow query + + + + Failed query execution + + + diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java new file mode 100644 index 0000000..9044bae --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java @@ -0,0 +1,960 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool.jmx; + +import java.util.Properties; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jdbc.pool.PoolConfiguration; +import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorDefinition; +import org.apache.tomcat.jdbc.pool.PoolUtilities; +import org.apache.tomcat.jdbc.pool.Validator; + +public class ConnectionPool extends NotificationBroadcasterSupport implements ConnectionPoolMBean { + /** + * logger + */ + private static final Log log = LogFactory.getLog(ConnectionPool.class); + + /** + * the connection pool + */ + protected org.apache.tomcat.jdbc.pool.ConnectionPool pool = null; + /** + * sequence for JMX notifications + */ + protected AtomicInteger sequence = new AtomicInteger(0); + + /** + * Listeners that are local and interested in our notifications, no need for JMX + */ + protected ConcurrentLinkedQueue listeners = + new ConcurrentLinkedQueue<>(); + + public ConnectionPool(org.apache.tomcat.jdbc.pool.ConnectionPool pool) { + super(); + this.pool = pool; + } + + public org.apache.tomcat.jdbc.pool.ConnectionPool getPool() { + return pool; + } + + public PoolConfiguration getPoolProperties() { + return pool.getPoolProperties(); + } + + //================================================================= + // NOTIFICATION INFO + //================================================================= + public static final String NOTIFY_INIT = "INIT FAILED"; + public static final String NOTIFY_CONNECT = "CONNECTION FAILED"; + public static final String NOTIFY_ABANDON = "CONNECTION ABANDONED"; + public static final String SLOW_QUERY_NOTIFICATION = "SLOW QUERY"; + public static final String FAILED_QUERY_NOTIFICATION = "FAILED QUERY"; + public static final String SUSPECT_ABANDONED_NOTIFICATION = "SUSPECT CONNECTION ABANDONED"; + public static final String POOL_EMPTY = "POOL EMPTY"; + public static final String SUSPECT_RETURNED_NOTIFICATION = "SUSPECT CONNECTION RETURNED"; + + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + MBeanNotificationInfo[] pres = super.getNotificationInfo(); + MBeanNotificationInfo[] loc = getDefaultNotificationInfo(); + MBeanNotificationInfo[] aug = new MBeanNotificationInfo[pres.length + loc.length]; + if (pres.length>0) System.arraycopy(pres, 0, aug, 0, pres.length); + if (loc.length >0) System.arraycopy(loc, 0, aug, pres.length, loc.length); + return aug; + } + + public static MBeanNotificationInfo[] getDefaultNotificationInfo() { + String[] types = new String[] {NOTIFY_INIT, NOTIFY_CONNECT, NOTIFY_ABANDON, SLOW_QUERY_NOTIFICATION, + FAILED_QUERY_NOTIFICATION, SUSPECT_ABANDONED_NOTIFICATION, POOL_EMPTY, SUSPECT_RETURNED_NOTIFICATION}; + String name = Notification.class.getName(); + String description = "A connection pool error condition was met."; + MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description); + return new MBeanNotificationInfo[] {info}; + } + + /** + * Return true if the notification was sent successfully, false otherwise. + * @param type The notification type + * @param message The message + * @return true if the notification succeeded + */ + public boolean notify(final String type, String message) { + try { + Notification n = new Notification( + type, + this, + sequence.incrementAndGet(), + System.currentTimeMillis(), + "["+type+"] "+message); + sendNotification(n); + for (NotificationListener listener : listeners) { + listener.handleNotification(n,this); + } + return true; + }catch (Exception x) { + if (log.isDebugEnabled()) { + log.debug("Notify failed. Type="+type+"; Message="+message,x); + } + return false; + } + + } + + public void addListener(NotificationListener list) { + listeners.add(list); + } + + public boolean removeListener(NotificationListener list) { + return listeners.remove(list); + } + + //================================================================= + // POOL STATS + //================================================================= + + @Override + public int getSize() { + return pool.getSize(); + } + + @Override + public int getIdle() { + return pool.getIdle(); + } + + @Override + public int getActive() { + return pool.getActive(); + } + + @Override + public int getNumIdle() { + return getIdle(); + } + + @Override + public int getNumActive() { + return getActive(); + } + + @Override + public int getWaitCount() { + return pool.getWaitCount(); + } + + @Override + public long getBorrowedCount() { + return pool.getBorrowedCount(); + } + + @Override + public long getReturnedCount() { + return pool.getReturnedCount(); + } + + @Override + public long getCreatedCount() { + return pool.getCreatedCount(); + } + + @Override + public long getReleasedCount() { + return pool.getReleasedCount(); + } + + @Override + public long getReconnectedCount() { + return pool.getReconnectedCount(); + } + + @Override + public long getRemoveAbandonedCount() { + return pool.getRemoveAbandonedCount(); + } + + @Override + public long getReleasedIdleCount() { + return pool.getReleasedIdleCount(); + } + + //================================================================= + // POOL OPERATIONS + //================================================================= + @Override + public void checkIdle() { + pool.checkIdle(); + } + + @Override + public void checkAbandoned() { + pool.checkAbandoned(); + } + + @Override + public void testIdle() { + pool.testAllIdle(); + } + + @Override + public void resetStats() { + pool.resetStats(); + } + + //================================================================= + // POOL PROPERTIES + //================================================================= + //========================================================= + // PROPERTIES / CONFIGURATION + //========================================================= + + + @Override + public String getConnectionProperties() { + return getPoolProperties().getConnectionProperties(); + } + + @Override + public Properties getDbProperties() { + return PoolUtilities.cloneWithoutPassword(getPoolProperties().getDbProperties()); + } + + @Override + public String getDefaultCatalog() { + return getPoolProperties().getDefaultCatalog(); + } + + @Override + public int getDefaultTransactionIsolation() { + return getPoolProperties().getDefaultTransactionIsolation(); + } + + @Override + public String getDriverClassName() { + return getPoolProperties().getDriverClassName(); + } + + + @Override + public int getInitialSize() { + return getPoolProperties().getInitialSize(); + } + + @Override + public String getInitSQL() { + return getPoolProperties().getInitSQL(); + } + + @Override + public String getJdbcInterceptors() { + return getPoolProperties().getJdbcInterceptors(); + } + + @Override + public int getMaxActive() { + return getPoolProperties().getMaxActive(); + } + + @Override + public int getMaxIdle() { + return getPoolProperties().getMaxIdle(); + } + + @Override + public int getMaxWait() { + return getPoolProperties().getMaxWait(); + } + + @Override + public int getMinEvictableIdleTimeMillis() { + return getPoolProperties().getMinEvictableIdleTimeMillis(); + } + + @Override + public int getMinIdle() { + return getPoolProperties().getMinIdle(); + } + + @Override + public long getMaxAge() { + return getPoolProperties().getMaxAge(); + } + + @Override + public String getName() { + return this.getPoolName(); + } + + @Override + public int getNumTestsPerEvictionRun() { + return getPoolProperties().getNumTestsPerEvictionRun(); + } + + /** + * @return DOES NOT RETURN THE PASSWORD, IT WOULD SHOW UP IN JMX + */ + @Override + public String getPassword() { + return "Password not available as DataSource/JMX operation."; + } + + @Override + public int getRemoveAbandonedTimeout() { + return getPoolProperties().getRemoveAbandonedTimeout(); + } + + + @Override + public int getTimeBetweenEvictionRunsMillis() { + return getPoolProperties().getTimeBetweenEvictionRunsMillis(); + } + + @Override + public String getUrl() { + return getPoolProperties().getUrl(); + } + + @Override + public String getUsername() { + return getPoolProperties().getUsername(); + } + + @Override + public long getValidationInterval() { + return getPoolProperties().getValidationInterval(); + } + + @Override + public String getValidationQuery() { + return getPoolProperties().getValidationQuery(); + } + + @Override + public int getValidationQueryTimeout() { + return getPoolProperties().getValidationQueryTimeout(); + } + + /** + * {@inheritDoc} + */ + + @Override + public String getValidatorClassName() { + return getPoolProperties().getValidatorClassName(); + } + + /** + * {@inheritDoc} + */ + + @Override + public Validator getValidator() { + return getPoolProperties().getValidator(); + } + + @Override + public boolean isAccessToUnderlyingConnectionAllowed() { + return getPoolProperties().isAccessToUnderlyingConnectionAllowed(); + } + + @Override + public Boolean isDefaultAutoCommit() { + return getPoolProperties().isDefaultAutoCommit(); + } + + @Override + public Boolean isDefaultReadOnly() { + return getPoolProperties().isDefaultReadOnly(); + } + + @Override + public boolean isLogAbandoned() { + return getPoolProperties().isLogAbandoned(); + } + + @Override + public boolean isPoolSweeperEnabled() { + return getPoolProperties().isPoolSweeperEnabled(); + } + + @Override + public boolean isRemoveAbandoned() { + return getPoolProperties().isRemoveAbandoned(); + } + + @Override + public int getAbandonWhenPercentageFull() { + return getPoolProperties().getAbandonWhenPercentageFull(); + } + + @Override + public boolean isTestOnBorrow() { + return getPoolProperties().isTestOnBorrow(); + } + + @Override + public boolean isTestOnConnect() { + return getPoolProperties().isTestOnConnect(); + } + + @Override + public boolean isTestOnReturn() { + return getPoolProperties().isTestOnReturn(); + } + + @Override + public boolean isTestWhileIdle() { + return getPoolProperties().isTestWhileIdle(); + } + + + @Override + public Boolean getDefaultAutoCommit() { + return getPoolProperties().getDefaultAutoCommit(); + } + + @Override + public Boolean getDefaultReadOnly() { + return getPoolProperties().getDefaultReadOnly(); + } + + @Override + public InterceptorDefinition[] getJdbcInterceptorsAsArray() { + return getPoolProperties().getJdbcInterceptorsAsArray(); + } + + @Override + public boolean getUseLock() { + return getPoolProperties().getUseLock(); + } + + @Override + public boolean isFairQueue() { + return getPoolProperties().isFairQueue(); + } + + @Override + public boolean isJmxEnabled() { + return getPoolProperties().isJmxEnabled(); + } + + @Override + public boolean isUseEquals() { + return getPoolProperties().isUseEquals(); + } + + @Override + public void setAbandonWhenPercentageFull(int percentage) { + getPoolProperties().setAbandonWhenPercentageFull(percentage); + } + + @Override + public void setAccessToUnderlyingConnectionAllowed(boolean accessToUnderlyingConnectionAllowed) { + getPoolProperties().setAccessToUnderlyingConnectionAllowed(accessToUnderlyingConnectionAllowed); + } + + @Override + public void setDbProperties(Properties dbProperties) { + getPoolProperties().setDbProperties(dbProperties); + } + + @Override + public void setDefaultReadOnly(Boolean defaultReadOnly) { + getPoolProperties().setDefaultReadOnly(defaultReadOnly); + } + + @Override + public void setMaxAge(long maxAge) { + getPoolProperties().setMaxAge(maxAge); + } + + @Override + public void setName(String name) { + getPoolProperties().setName(name); + } + + @Override + public String getPoolName() { + return getPoolProperties().getName(); + } + + + @Override + public void setConnectionProperties(String connectionProperties) { + getPoolProperties().setConnectionProperties(connectionProperties); + + } + + @Override + public void setDefaultAutoCommit(Boolean defaultAutoCommit) { + getPoolProperties().setDefaultAutoCommit(defaultAutoCommit); + } + + @Override + public void setDefaultCatalog(String defaultCatalog) { + getPoolProperties().setDefaultCatalog(defaultCatalog); + } + + @Override + public void setDefaultTransactionIsolation(int defaultTransactionIsolation) { + getPoolProperties().setDefaultTransactionIsolation(defaultTransactionIsolation); + } + + @Override + public void setDriverClassName(String driverClassName) { + getPoolProperties().setDriverClassName(driverClassName); + } + + + @Override + public void setFairQueue(boolean fairQueue) { + // noop - this pool is already running + throw new UnsupportedOperationException(); + } + + + @Override + public void setInitialSize(int initialSize) { + // noop - this pool is already running + throw new UnsupportedOperationException(); + + } + + + @Override + public void setInitSQL(String initSQL) { + getPoolProperties().setInitSQL(initSQL); + + } + + + @Override + public void setJdbcInterceptors(String jdbcInterceptors) { + // noop - this pool is already running + throw new UnsupportedOperationException(); + } + + + @Override + public void setJmxEnabled(boolean jmxEnabled) { + // noop - this pool is already running and obviously jmx enabled + throw new UnsupportedOperationException(); + } + + + @Override + public void setLogAbandoned(boolean logAbandoned) { + getPoolProperties().setLogAbandoned(logAbandoned); + } + + + @Override + public void setMaxActive(int maxActive) { + getPoolProperties().setMaxActive(maxActive); + //make sure the pool is properly configured + pool.checkPoolConfiguration(getPoolProperties()); + } + + + @Override + public void setMaxIdle(int maxIdle) { + getPoolProperties().setMaxIdle(maxIdle); + //make sure the pool is properly configured + pool.checkPoolConfiguration(getPoolProperties()); + + } + + + @Override + public void setMaxWait(int maxWait) { + getPoolProperties().setMaxWait(maxWait); + } + + + @Override + public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) { + boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled(); + getPoolProperties().setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + boolean shouldBeEnabled = getPoolProperties().isPoolSweeperEnabled(); + //make sure pool cleaner starts/stops when it should + if (!wasEnabled && shouldBeEnabled) pool.initializePoolCleaner(getPoolProperties()); + else if (wasEnabled && !shouldBeEnabled) pool.terminatePoolCleaner(); + } + + + @Override + public void setMinIdle(int minIdle) { + getPoolProperties().setMinIdle(minIdle); + //make sure the pool is properly configured + pool.checkPoolConfiguration(getPoolProperties()); + } + + + @Override + public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { + getPoolProperties().setNumTestsPerEvictionRun(numTestsPerEvictionRun); + } + + + @Override + public void setPassword(String password) { + getPoolProperties().setPassword(password); + } + + + @Override + public void setRemoveAbandoned(boolean removeAbandoned) { + boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled(); + getPoolProperties().setRemoveAbandoned(removeAbandoned); + boolean shouldBeEnabled = getPoolProperties().isPoolSweeperEnabled(); + //make sure pool cleaner starts/stops when it should + if (!wasEnabled && shouldBeEnabled) pool.initializePoolCleaner(getPoolProperties()); + else if (wasEnabled && !shouldBeEnabled) pool.terminatePoolCleaner(); + } + + + @Override + public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) { + boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled(); + getPoolProperties().setRemoveAbandonedTimeout(removeAbandonedTimeout); + boolean shouldBeEnabled = getPoolProperties().isPoolSweeperEnabled(); + //make sure pool cleaner starts/stops when it should + if (!wasEnabled && shouldBeEnabled) pool.initializePoolCleaner(getPoolProperties()); + else if (wasEnabled && !shouldBeEnabled) pool.terminatePoolCleaner(); + } + + + @Override + public void setTestOnBorrow(boolean testOnBorrow) { + getPoolProperties().setTestOnBorrow(testOnBorrow); + } + + + @Override + public void setTestOnConnect(boolean testOnConnect) { + getPoolProperties().setTestOnConnect(testOnConnect); + } + + + @Override + public void setTestOnReturn(boolean testOnReturn) { + getPoolProperties().setTestOnReturn(testOnReturn); + } + + + @Override + public void setTestWhileIdle(boolean testWhileIdle) { + boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled(); + getPoolProperties().setTestWhileIdle(testWhileIdle); + boolean shouldBeEnabled = getPoolProperties().isPoolSweeperEnabled(); + //make sure pool cleaner starts/stops when it should + if (!wasEnabled && shouldBeEnabled) pool.initializePoolCleaner(getPoolProperties()); + else if (wasEnabled && !shouldBeEnabled) pool.terminatePoolCleaner(); + } + + + @Override + public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) { + boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled(); + getPoolProperties().setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + boolean shouldBeEnabled = getPoolProperties().isPoolSweeperEnabled(); + //make sure pool cleaner starts/stops when it should + if (!wasEnabled && shouldBeEnabled) { + pool.initializePoolCleaner(getPoolProperties()); + } else if (wasEnabled) { + pool.terminatePoolCleaner(); + if (shouldBeEnabled) { + pool.initializePoolCleaner(getPoolProperties()); + } + } + } + + + @Override + public void setUrl(String url) { + getPoolProperties().setUrl(url); + } + + + @Override + public void setUseEquals(boolean useEquals) { + getPoolProperties().setUseEquals(useEquals); + } + + + @Override + public void setUseLock(boolean useLock) { + getPoolProperties().setUseLock(useLock); + } + + + @Override + public void setUsername(String username) { + getPoolProperties().setUsername(username); + } + + + @Override + public void setValidationInterval(long validationInterval) { + getPoolProperties().setValidationInterval(validationInterval); + } + + + @Override + public void setValidationQuery(String validationQuery) { + getPoolProperties().setValidationQuery(validationQuery); + } + + @Override + public void setValidationQueryTimeout(int validationQueryTimeout) { + getPoolProperties().setValidationQueryTimeout(validationQueryTimeout); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setValidatorClassName(String className) { + getPoolProperties().setValidatorClassName(className); + } + + /** + * {@inheritDoc} + */ + + @Override + public int getSuspectTimeout() { + return getPoolProperties().getSuspectTimeout(); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setSuspectTimeout(int seconds) { + getPoolProperties().setSuspectTimeout(seconds); + } + + /** + * {@inheritDoc} + */ + @Override + public void setDataSource(Object ds) { + getPoolProperties().setDataSource(ds); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getDataSource() { + return getPoolProperties().getDataSource(); + } + + + /** + * {@inheritDoc} + */ + @Override + public void setDataSourceJNDI(String jndiDS) { + getPoolProperties().setDataSourceJNDI(jndiDS); + } + + /** + * {@inheritDoc} + */ + @Override + public String getDataSourceJNDI() { + return getPoolProperties().getDataSourceJNDI(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAlternateUsernameAllowed() { + return getPoolProperties().isAlternateUsernameAllowed(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setAlternateUsernameAllowed(boolean alternateUsernameAllowed) { + getPoolProperties().setAlternateUsernameAllowed(alternateUsernameAllowed); + } + + /** + * {@inheritDoc} + */ + @Override + public void setValidator(Validator validator) { + getPoolProperties().setValidator(validator); + } + + /** + * {@inheritDoc} + */ + @Override + public void setCommitOnReturn(boolean commitOnReturn) { + getPoolProperties().setCommitOnReturn(commitOnReturn); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getCommitOnReturn() { + return getPoolProperties().getCommitOnReturn(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setRollbackOnReturn(boolean rollbackOnReturn) { + getPoolProperties().setRollbackOnReturn(rollbackOnReturn); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getRollbackOnReturn() { + return getPoolProperties().getRollbackOnReturn(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setUseDisposableConnectionFacade(boolean useDisposableConnectionFacade) { + getPoolProperties().setUseDisposableConnectionFacade(useDisposableConnectionFacade); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getUseDisposableConnectionFacade() { + return getPoolProperties().getUseDisposableConnectionFacade(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setLogValidationErrors(boolean logValidationErrors) { + getPoolProperties().setLogValidationErrors(logValidationErrors); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getLogValidationErrors() { + return getPoolProperties().getLogValidationErrors(); + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean getPropagateInterruptState() { + return getPoolProperties().getPropagateInterruptState(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setPropagateInterruptState(boolean propagateInterruptState) { + getPoolProperties().setPropagateInterruptState(propagateInterruptState); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isIgnoreExceptionOnPreLoad() { + return getPoolProperties().isIgnoreExceptionOnPreLoad(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setIgnoreExceptionOnPreLoad(boolean ignoreExceptionOnPreLoad) { + // noop - this pool is already running + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + */ + @Override + public void purge() { + pool.purge(); + + } + + /** + * {@inheritDoc} + */ + @Override + public void purgeOnReturn() { + pool.purgeOnReturn(); + + } + + + + + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java new file mode 100644 index 0000000..bc3bbca --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java @@ -0,0 +1,107 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool.jmx; + +import org.apache.tomcat.jdbc.pool.PoolConfiguration; + +public interface ConnectionPoolMBean extends PoolConfiguration { + + //================================================================= + // POOL STATS + //================================================================= + + public int getSize(); + + public int getIdle(); + + public int getActive(); + + public int getNumIdle(); + + public int getNumActive(); + + public int getWaitCount(); + + public long getBorrowedCount(); + + public long getReturnedCount(); + + public long getCreatedCount(); + + public long getReleasedCount(); + + public long getReconnectedCount(); + + public long getRemoveAbandonedCount(); + + public long getReleasedIdleCount(); + + //================================================================= + // POOL OPERATIONS + //================================================================= + public void checkIdle(); + + public void checkAbandoned(); + + public void testIdle(); + + /** + * Purges all connections in the pool. + * For connections currently in use, these connections will be + * purged when returned on the pool. This call also + * purges connections that are idle and in the pool + * To only purge used/active connections see {@link #purgeOnReturn()} + */ + public void purge(); + + /** + * Purges connections when they are returned from the pool. + * This call does not purge idle connections until they are used. + * To purge idle connections see {@link #purge()} + */ + public void purgeOnReturn(); + + /** + * reset the statistics of this pool. + */ + public void resetStats(); + + //================================================================= + // POOL NOTIFICATIONS + //================================================================= + + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml new file mode 100644 index 0000000..591c1d1 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml @@ -0,0 +1,420 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dblib/common/src/main/java/org/openecomp/sdnc/sli/resource/common/CommonActivator.java b/dblib/common/src/main/java/org/openecomp/sdnc/sli/resource/common/CommonActivator.java new file mode 100644 index 0000000..6a0ed06 --- /dev/null +++ b/dblib/common/src/main/java/org/openecomp/sdnc/sli/resource/common/CommonActivator.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.resource.common; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class CommonActivator implements BundleActivator { + + @Override + public void start(BundleContext context) throws Exception { + // TODO Auto-generated method stub + + } + + @Override + public void stop(BundleContext context) throws Exception { + // TODO Auto-generated method stub + + } + +} diff --git a/dblib/features/src/main/resources/features.xml b/dblib/features/src/main/resources/features.xml index cc080aa..d7f0030 100755 --- a/dblib/features/src/main/resources/features.xml +++ b/dblib/features/src/main/resources/features.xml @@ -10,6 +10,7 @@ odl-mdsal-broker + mvn:org.openecomp.sdnc.core/dblib-common/${project.version} mvn:org.openecomp.sdnc.core/dblib-provider/${project.version} mvn:mysql/mysql-connector-java/${mysql.connector.version} diff --git a/dblib/pom.xml b/dblib/pom.xml index 0534e07..5adb663 100755 --- a/dblib/pom.xml +++ b/dblib/pom.xml @@ -119,6 +119,7 @@ AT&T + common provider features installer diff --git a/dblib/provider/pom.xml b/dblib/provider/pom.xml index 71de996..913adf3 100755 --- a/dblib/provider/pom.xml +++ b/dblib/provider/pom.xml @@ -47,6 +47,11 @@ mysql-connector-java ${mysql.connector.version} + + org.openecomp.sdnc.core + dblib-common + ${project.version} + @@ -61,7 +66,10 @@ org.openecomp.sdnc.sli.resource.dblib org.openecomp.sdnc.sli.resource.dblib.DBLIBResourceActivator org.openecomp.sdnc.sli.resource.dblib;version=${project.version} - org.openecomp.sdnc.sli.*,org.osgi.framework.*,org.slf4j.*,com.mysql.jdbc.*,javax.sql.* + + * true diff --git a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/CachedDataSourceFactory.java b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/CachedDataSourceFactory.java index f774748..3e51ed9 100644 --- a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/CachedDataSourceFactory.java +++ b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/CachedDataSourceFactory.java @@ -23,9 +23,8 @@ package org.openecomp.sdnc.sli.resource.dblib; import org.openecomp.sdnc.sli.resource.dblib.config.BaseDBConfiguration; import org.openecomp.sdnc.sli.resource.dblib.config.JDBCConfiguration; -import org.openecomp.sdnc.sli.resource.dblib.config.JndiConfiguration; +import org.openecomp.sdnc.sli.resource.dblib.jdbc.JdbcDBCachedDataSource; import org.openecomp.sdnc.sli.resource.dblib.jdbc.MySQLCachedDataSource; -import org.openecomp.sdnc.sli.resource.dblib.jndi.JndiCachedDataSource; /** * @version $Revision: 1.1 $ @@ -38,9 +37,8 @@ public class CachedDataSourceFactory { public static CachedDataSource createDataSource(BaseDBConfiguration config) { if(config instanceof JDBCConfiguration) - return MySQLCachedDataSource.createInstance(config); - if(config instanceof JndiConfiguration) - return JndiCachedDataSource.createInstance(config); + return JdbcDBCachedDataSource.createInstance(config); + return (CachedDataSource)null; } diff --git a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/DBResourceManager.java b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/DBResourceManager.java index 7f85c42..401c013 100644 --- a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/DBResourceManager.java +++ b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/DBResourceManager.java @@ -53,7 +53,6 @@ import org.openecomp.sdnc.sli.resource.dblib.pm.SQLExecutionMonitor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.sun.rowset.providers.RIOptimisticProvider; /** * @version $Revision: 1.15 $ @@ -87,7 +86,7 @@ public class DBResourceManager implements DataSource, DataAccessor, DBResourceOb } return 0; } - + }); protected final Set broken = Collections.synchronizedSet(new HashSet()); protected final Object monitor = new Object(); @@ -131,17 +130,6 @@ public class DBResourceManager implements DataSource, DataAccessor, DBResourceOb worker.setName("DBResourcemanagerWatchThread"); worker.setDaemon(true); worker.start(); - - try { - RIOptimisticProvider rio = new RIOptimisticProvider(); - LOGGER.info("Class " + rio.getClass().getName() + " found."); - Class clas = this.getClass().getClassLoader().loadClass("com.sun.rowset.providers.RIOptimisticProvider"); - if(clas != null) { - LOGGER.info("Class " + rio.getClass().getName() + " found by classloader."); - } - } catch(Exception exc) { - LOGGER.info("Failed resolving RIOptimisticProvider class", exc); - } } private void config(Properties ctx) throws Exception { @@ -320,7 +308,7 @@ public class DBResourceManager implements DataSource, DataAccessor, DBResourceOb Collections.reverse(sources); } - + // loop through available data sources to retrieve data. while(!sources.isEmpty()) { @@ -428,7 +416,7 @@ public class DBResourceManager implements DataSource, DataAccessor, DBResourceOb public boolean writeData(String statement, ArrayList arguments, String preferredDS) throws SQLException { return writeDataNoRecovery(statement, arguments, preferredDS); } - + CachedDataSource findMaster() { CachedDataSource master = null; CachedDataSource[] dss = this.dsQueue.toArray(new CachedDataSource[0]); @@ -468,7 +456,7 @@ public class DBResourceManager implements DataSource, DataAccessor, DBResourceOb active = master; } } - + return active.writeData(statement, arguments); } catch(Throwable exc){ String message = exc.getMessage(); diff --git a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/config/DbConfigPool.java b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/config/DbConfigPool.java index 90ea6bc..117f932 100644 --- a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/config/DbConfigPool.java +++ b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/config/DbConfigPool.java @@ -46,10 +46,6 @@ public class DbConfigPool { return type; } - public JndiConfiguration[] getJndiDbSourceArray() { - return configurations.toArray(new JndiConfiguration[configurations.size()]); - } - public JDBCConfiguration[] getJDBCbSourceArray() { return configurations.toArray(new JDBCConfiguration[configurations.size()]); } diff --git a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/AbstractDBResourceManagerFactory.java b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/AbstractDBResourceManagerFactory.java index e6b1a35..f4291a7 100644 --- a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/AbstractDBResourceManagerFactory.java +++ b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/AbstractDBResourceManagerFactory.java @@ -21,7 +21,6 @@ package org.openecomp.sdnc.sli.resource.dblib.factory; import org.openecomp.sdnc.sli.resource.dblib.jdbc.JdbcDbResourceManagerFactory; -import org.openecomp.sdnc.sli.resource.dblib.jndi.JNDIDbResourceManagerFactory; /** * @version $Revision: 1.1 $ @@ -33,13 +32,7 @@ import org.openecomp.sdnc.sli.resource.dblib.jndi.JNDIDbResourceManagerFactory; public class AbstractDBResourceManagerFactory { public static AbstractResourceManagerFactory getFactory(String type) throws FactoryNotDefinedException { - - if("JNDI".equals(type)){ - try { - return JNDIDbResourceManagerFactory.createIntstance(); - } catch (Exception e) { - } - } + // JDBC return JdbcDbResourceManagerFactory.createIntstance(); } diff --git a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/AbstractResourceManagerFactory.java b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/AbstractResourceManagerFactory.java index 148ddac..29ec061 100644 --- a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/AbstractResourceManagerFactory.java +++ b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/AbstractResourceManagerFactory.java @@ -32,7 +32,6 @@ import org.openecomp.sdnc.sli.resource.dblib.DBResourceManager; import org.openecomp.sdnc.sli.resource.dblib.config.BaseDBConfiguration; import org.openecomp.sdnc.sli.resource.dblib.config.DbConfigPool; import org.openecomp.sdnc.sli.resource.dblib.config.JDBCConfiguration; -import org.openecomp.sdnc.sli.resource.dblib.config.JndiConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,20 +48,15 @@ public abstract class AbstractResourceManagerFactory { public abstract CachedDataSource[] initDBResourceManager(DbConfigPool dbConfig, DBResourceManager manager) throws Exception; public abstract CachedDataSource[] initDBResourceManager(DbConfigPool dbConfig, DBResourceManager dbResourceManager, String sourceName) throws SQLException ; - + public static AbstractResourceManagerFactory createIntstance() throws FactoryNotDefinedException { throw new FactoryNotDefinedException("Factory method 'createIntstance' needs to be overriden in DBResourceManagerFactory"); } - public class DBInitTask implements Callable + public class DBInitTask implements Callable { private BaseDBConfiguration config = null; private Set activeTasks; - - public DBInitTask(JndiConfiguration jndiConfig, Set tasks){ - this.config = jndiConfig; - this.activeTasks = tasks; - } public DBInitTask(JDBCConfiguration jdbcconfig, Set tasks) { this.config = jdbcconfig; @@ -97,6 +91,7 @@ public abstract class AbstractResourceManagerFactory { worker.start(); } else { if(LOGGER.isDebugEnabled()) + if(ds != null) LOGGER.debug("Completed CachedDataSource.Call from "+ds.getDbConnectionName()); } } diff --git a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/DBConfigFactory.java b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/DBConfigFactory.java index 7044082..3530459 100644 --- a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/DBConfigFactory.java +++ b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/factory/DBConfigFactory.java @@ -25,10 +25,11 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.Properties; +import org.slf4j.LoggerFactory; + import org.openecomp.sdnc.sli.resource.dblib.config.BaseDBConfiguration; import org.openecomp.sdnc.sli.resource.dblib.config.DbConfigPool; import org.openecomp.sdnc.sli.resource.dblib.config.JDBCConfiguration; -import org.openecomp.sdnc.sli.resource.dblib.config.JndiConfiguration; /** * @version $Revision: 1.1 $ @@ -44,7 +45,7 @@ public class DBConfigFactory { } static DbConfigPool getConfigparams(Properties properties){ - org.slf4j.LoggerFactory.getLogger(DBConfigFactory.class).info(properties.toString()); + LoggerFactory.getLogger(DBConfigFactory.class).info(properties.toString()); DbConfigPool xmlConfig = new DbConfigPool(properties); ArrayList propertySets = new ArrayList(); @@ -78,7 +79,7 @@ public class DBConfigFactory { } } catch (Exception e) { - org.slf4j.LoggerFactory.getLogger(DBConfigFactory.class).warn("",e); + LoggerFactory.getLogger(DBConfigFactory.class).warn("",e); } return xmlConfig; @@ -89,15 +90,12 @@ public class DBConfigFactory { String type = props.getProperty(BaseDBConfiguration.DATABASE_TYPE); BaseDBConfiguration config = null; - - if("JNDI".equalsIgnoreCase(type)) { - config = new JndiConfiguration(props); - } + if("JDBC".equalsIgnoreCase(type)) { config = new JDBCConfiguration(props); } return config; - + } } diff --git a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jdbc/JdbcDBCachedDataSource.java b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jdbc/JdbcDBCachedDataSource.java new file mode 100644 index 0000000..ee3d4ff --- /dev/null +++ b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jdbc/JdbcDBCachedDataSource.java @@ -0,0 +1,252 @@ +/*- + * ============LICENSE_START======================================================= + * openecomp + * ================================================================================ + * Copyright (C) 2016 - 2017 AT&T + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.resource.dblib.jdbc; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLFeatureNotSupportedException; + +import org.apache.tomcat.jdbc.pool.DataSource; +import org.apache.tomcat.jdbc.pool.PoolProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.openecomp.sdnc.sli.resource.dblib.CachedDataSource; +import org.openecomp.sdnc.sli.resource.dblib.DBConfigException; +import org.openecomp.sdnc.sli.resource.dblib.config.BaseDBConfiguration; +import org.openecomp.sdnc.sli.resource.dblib.config.JDBCConfiguration; +import com.mysql.jdbc.Driver; + + +/** + * @version $Revision: 1.7 $ + * Change Log + * Author Date Comments + * ============== ======== ==================================================== + * Rich Tabedzki + */ + +public class JdbcDBCachedDataSource extends CachedDataSource +{ + private String dbUserId; + private String dbPasswd; + private String dbUrl; + + private int minLimit; + private int maxLimit; + private int initialLimit; + + private static final String AS_CONF_ERROR = "AS_CONF_ERROR: "; + + private static Logger LOGGER = LoggerFactory.getLogger(JdbcDBCachedDataSource.class); + + /** + * @param jdbcElem + * @param alarmLog + * @param occManager + * @throws Exception + */ + public JdbcDBCachedDataSource(BaseDBConfiguration jdbcElem) + { + super(jdbcElem); + } + + @Override + protected void configure(BaseDBConfiguration xmlElem) throws DBConfigException + { + BaseDBConfiguration jdbcConfig = (BaseDBConfiguration)xmlElem; + if(jdbcConfig.getConnTimeout() > 0){ + this.CONN_REQ_TIMEOUT = jdbcConfig.getConnTimeout(); + } + if(jdbcConfig.getRequestTimeout() > 0){ + this.DATA_REQ_TIMEOUT = jdbcConfig.getRequestTimeout(); + } + + // set connection pool name + String dbConnectionName = jdbcConfig.getDbConnectionName(); + super.setDbConnectionName(dbConnectionName); + // Configure the JDBC connection + dbUserId = jdbcConfig.getDbUserId(); + if (dbUserId == null) + { + String errorMsg = "Invalid XML contents: JDBCConnection missing dbUserId attribute"; + LOGGER.error(AS_CONF_ERROR + errorMsg); + throw new DBConfigException(errorMsg); + } + + dbPasswd = jdbcConfig.getDbPasswd(); + if (dbPasswd == null) + { + String errorMsg = "Invalid XML contents: JDBCConnection missing dbPasswd attribute"; + LOGGER.error(AS_CONF_ERROR + errorMsg); + throw new DBConfigException(errorMsg); + } + /* + dbDriver = jdbcConfig.getDbDriver(); + if (dbDriver == null) + { + String errorMsg = "Invalid XML contents: JDBCConnection missing dbDriver attribute"; + LOGGER.error(AS_CONF_ERROR + errorMsg); + throw new ScpTblUpdateError(errorMsg); + } + */ + + minLimit = jdbcConfig.getDbMinLimit(); +// if (minLimit == null) +// { +// String errorMsg = "Invalid XML contents: JDBC Connection missing minLimit attribute"; +// LOGGER.error(AS_CONF_ERROR + errorMsg); +// throw new DBConfigException(errorMsg); +// } + maxLimit = jdbcConfig.getDbMaxLimit(); +// if (maxLimit == null) +// { +// String errorMsg = "Invalid XML contents: JDBC Connection missing maxLimit attribute"; +// LOGGER.error(AS_CONF_ERROR + errorMsg); +// throw new DBConfigException(errorMsg); +// } + initialLimit = jdbcConfig.getDbInitialLimit(); +// if (initialLimit == null) +// { +// String errorMsg = "Invalid XML contents: JDBC Connection missing initialLimit attribute"; +// LOGGER.error(AS_CONF_ERROR + errorMsg); +// throw new DBConfigException(errorMsg); +// } + + dbUrl = jdbcConfig.getDbUrl(); + if(dbUrl == null){ + String errorMsg = "Invalid XML contents: JDBCConnection missing dbUrl attribute"; + LOGGER.error(AS_CONF_ERROR + errorMsg); + throw new DBConfigException(errorMsg); + } + + try { + Driver dr = new com.mysql.jdbc.Driver(); + Class clazz = Class.forName("com.mysql.jdbc.Driver") ; + + PoolProperties p = new PoolProperties(); + p.setDriverClassName("com.mysql.jdbc.Driver"); + p.setUrl(dbUrl); + p.setUsername(dbUserId); + p.setPassword(dbPasswd); + p.setJmxEnabled(true); + p.setTestWhileIdle(false); + p.setTestOnBorrow(true); + p.setValidationQuery("SELECT 1"); + p.setTestOnReturn(false); + p.setValidationInterval(30000); + p.setTimeBetweenEvictionRunsMillis(30000); + p.setInitialSize(initialLimit); + p.setMaxActive(maxLimit); + p.setMaxIdle(maxLimit); + p.setMaxWait(10000); + p.setRemoveAbandonedTimeout(60); + p.setMinEvictableIdleTimeMillis(30000); + p.setMinIdle(minLimit); + p.setLogAbandoned(true); + p.setRemoveAbandoned(true); + p.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;" + + "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"); + + DataSource dataSource = new DataSource(p); + + synchronized(this) + { + this.ds = dataSource; + Connection con = null; + PreparedStatement st = null; + ResultSet rs = null; + + try { + con = dataSource.getConnection(); + st = con.prepareStatement("Select 1 FROM DUAL"); + rs = st.executeQuery(); + } catch(Exception exc) { + LOGGER.error(exc.getMessage()); + } finally { + if(rs != null) rs.close(); + if(st != null) st.close(); + if(con != null) con.close(); + } + + initialized = true; + LOGGER.info("MySQLDataSource <"+dbConnectionName+"> configured successfully. Using URL: "+dbUrl); + } + +// } catch (SQLException exc) { +// initialized = false; +// StringBuffer sb = new StringBuffer(); +// sb.append("Failed to initialize MySQLDataSource<"); +// sb.append(dbConnectionName).append(">. Reason: "); +// sb.append(exc.getMessage()); +// LOGGER.error("AS_CONF_ERROR: " + sb.toString()); +//// throw new DBConfigException(e.getMessage()); + } catch (Exception exc) { + initialized = false; + StringBuffer sb = new StringBuffer(); + sb.append("Failed to initialize MySQLCachedDataSource <"); + sb.append(dbConnectionName).append(">. Reason: "); + sb.append(exc.getMessage()); + LOGGER.error("AS_CONF_ERROR: " + sb.toString()); +// throw new DBConfigException(e.getMessage()); + } + } + + public final String getDbUrl() + { + return dbUrl; + } + + public final String getDbUserId() + { + return dbUserId; + } + + public final String getDbPasswd() + { + return dbPasswd; + } + + public static JdbcDBCachedDataSource createInstance(BaseDBConfiguration config) /*throws Exception*/ { + return new JdbcDBCachedDataSource(config); + } + + public String toString(){ + return getDbConnectionName(); + } + + public java.util.logging.Logger getParentLogger() + throws SQLFeatureNotSupportedException { + // TODO Auto-generated method stub + return null; + } + + public void cleanUp(){ + DataSource dataSource = (DataSource)ds; + dataSource.getPool().purge(); + int active = dataSource.getActive(); + int size = dataSource.getSize(); + dataSource.close(true); + super.cleanUp(); + } + +} diff --git a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jndi/JNDIDbResourceManagerFactory.java b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jndi/JNDIDbResourceManagerFactory.java deleted file mode 100644 index 2888bc5..0000000 --- a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jndi/JNDIDbResourceManagerFactory.java +++ /dev/null @@ -1,167 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * openecomp - * ================================================================================ - * Copyright (C) 2016 - 2017 AT&T - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= - */ - -package org.openecomp.sdnc.sli.resource.dblib.jndi; - -import java.sql.SQLException; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; - -import org.openecomp.sdnc.sli.resource.dblib.CachedDataSource; -import org.openecomp.sdnc.sli.resource.dblib.CachedDataSourceFactory; -import org.openecomp.sdnc.sli.resource.dblib.DBResourceManager; -import org.openecomp.sdnc.sli.resource.dblib.config.DbConfigPool; -import org.openecomp.sdnc.sli.resource.dblib.config.JndiConfiguration; -import org.openecomp.sdnc.sli.resource.dblib.factory.AbstractResourceManagerFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @version $Revision: 1.6 $ - * Change Log - * Author Date Comments - * ============== ======== ==================================================== - * Rich Tabedzki - */ -public class JNDIDbResourceManagerFactory extends AbstractResourceManagerFactory { - - private static Logger LOGGER = LoggerFactory.getLogger(JNDIDbResourceManagerFactory.class); - - class MyFutureTask extends FutureTask - { - - public MyFutureTask(Callable result) { - super((Callable)result); - } - - } - - public CachedDataSource[] initDBResourceManager(DbConfigPool dbConfig, DBResourceManager manager, String sourceName) throws SQLException - { - // here create the data sources objects - JndiConfiguration[] list = dbConfig.getJndiDbSourceArray(); - CachedDataSource[] cachedDS = new CachedDataSource[1]; - - for(int i=0, max=list.length; i[] futures = new MyFutureTask[list.length]; - final Set tasks = new HashSet(); - if(LOGGER.isDebugEnabled()) - LOGGER.debug("Creating datasources."); - for(int i=0, max=list.length; i0){ -// config.setJndiContextFactory(manager.getJndiContextFactoryStr()); -// } -// if(manager.getJndiURLStr()!=null && manager.getJndiURLStr().trim().length()>0){ -// config.setJndiURL(manager.getJndiURLStr()); -// } - DBInitTask task = new DBInitTask(config, tasks); - tasks.add(task); - futures[i] = new MyFutureTask(task); - } - - try { - synchronized(tasks){ - for(int i=0, max=list.length; i 0){ - this.CONN_REQ_TIMEOUT = jdbcConfig.getConnTimeout(); - } - if(jdbcConfig.getRequestTimeout() > 0){ - this.DATA_REQ_TIMEOUT = jdbcConfig.getRequestTimeout(); - } - - super.setDbConnectionName(jdbcConfig.getJndiConnectionName()); - - if(jndiContextFactoryStr == null || jndiContextFactoryStr.length() == 0) - { -// throw new DBConfigException("The jndi configuration is incomplete: jndiContextFactory"); - } - if(jndiURLStr == null || jndiContextFactoryStr.length() == 0) - { -// throw new ScpTblUpdateError("The jndi configuration is incomplete: jndiURL"); - } - if(jndiSourceStr == null || jndiSourceStr.length() == 0) - { - throw new DBConfigException("The jndi configuration is incomplete: jndiSource"); - } - - Properties env = new Properties(); - Context ctx; - try - { - if(jndiContextFactoryStr != null && jndiContextFactoryStr.length() != 0){ - env.put(Context.INITIAL_CONTEXT_FACTORY, jndiContextFactoryStr); - ctx = new InitialContext(env); - } else { - ctx = new InitialContext(); - } - ds = (javax.sql.DataSource) ctx.lookup (jndiSourceStr); - if(ds == null) - { - this.initialized = false; - LOGGER.error("AS_CONF_ERROR: Failed to initialize DataSource <"+getDbConnectionName()+"> using JNDI <"+jndiSourceStr+">"); - return; - } else { - this.initialized = true; - LOGGER.info("JndiCachedDataSource <"+getDbConnectionName()+"> configured successfully."); - return; - } - } catch (NamingException exc) { - this.initialized = false; - LOGGER.error("AS_CONF_ERROR" + exc.getMessage()); - - } catch(Throwable exc) { - this.initialized = false; - LOGGER.error("AS_CONF_ERROR: " + exc.getMessage()); - } - } - - public static JndiCachedDataSource createInstance(BaseDBConfiguration config) { - return new JndiCachedDataSource(config); - } - - public String toString(){ - return getDbConnectionName(); - } - - public java.util.logging.Logger getParentLogger() - throws SQLFeatureNotSupportedException { - // TODO Auto-generated method stub - return null; - } -} diff --git a/dblib/provider/src/main/resources/dblib.properties b/dblib/provider/src/main/resources/dblib.properties index 3872288..0f28f1f 100755 --- a/dblib/provider/src/main/resources/dblib.properties +++ b/dblib/provider/src/main/resources/dblib.properties @@ -3,7 +3,7 @@ org.openecomp.sdnc.sli.jdbc.hosts=sdnctldb01,sdnctldb02 org.openecomp.sdnc.sli.jdbc.url=jdbc:mysql://DBHOST:3306/sdnctl org.openecomp.sdnc.sli.jdbc.database=sdnctl org.openecomp.sdnc.sli.jdbc.user=sdnctl -org.openecomp.sdnc.sli.jdbc.password=gamma +org.openecomp.sdnc.sli.jdbc.password={password for sdnctl} org.openecomp.sdnc.sli.jdbc.connection.name=sdnctldb01 org.openecomp.sdnc.sli.jdbc.connection.timeout=50 diff --git a/filters/provider/pom.xml b/filters/provider/pom.xml index cf31a0c..6afa2d2 100755 --- a/filters/provider/pom.xml +++ b/filters/provider/pom.xml @@ -49,7 +49,11 @@ commons-codec ${commons.codec.version} - + + org.openecomp.sdnc.core + sli-common + ${sdnctl.sli.version} + + 1.7.1-Boron-SR1 + 0.4.1-Boron-SR1 + 1.0.1-Boron-SR1 + 1.4.1-Boron-SR1 + 2.1.1-Boron-SR1 + 0.9.1-Boron-SR1 + 0.9.1-Boron-SR1 + 1.4.1-Boron-SR1 + ${odl.mdsal.model.version} + 0.5.1-Boron-SR1 + ${odl.version} + ${odl.version} + 2010.09.24.9.1-Boron-SR1 + 2010.09.24.9.1-Boron-SR1 + 0.5.1-Boron-SR1 + ${odl.yangtools.version} + 0.9.1-Boron-SR1 + + + + 1.1.0-SNAPSHOT 1.1.0-SNAPSHOT + 1.1.0-SNAPSHOT + 1.1.0-SNAPSHOT + 1.1.0-SNAPSHOT ${sdnc.core.version} ${sdnc.adaptors.version} ${sdnc.core.version} ${sdnc.adaptors.version} ${sdnc.core.version} + + + + 1.10 + + 3.4 + + 2.6 + + 3.5 + + 2.6.2 + + 4.3.0 + + 2.3.1 + + 18.0 + + 1.3 + + 2.3.2 + + 3.20.0-GA + + + + 1.17 + + 1.17 + + + + + 20131018 + + + 1.3.7 + + 3.0.0 + + + 4.11 + + 1.1.7 + + 1.10.19 + + 4.0.37.Final + + 1.7.21 + + 3.2.14.RELEASE + + + + + 3.8.1.v20120830-144521 + ${jackson.version} + 1.3.7 + 0.6.4 + 4.4 + 4.5.1 + 5.1.39 + 1.4.186 + 4.0.0 @@ -154,7 +227,7 @@ org.opendaylight.odlparent odlparent - 1.6.2-Beryllium-SR2 + 1.7.1-Boron-SR1 org.slf4j @@ -215,7 +288,7 @@ org.opendaylight.mdsal yang-binding - ${odl.yangtools.version} + ${odl.mdsal.yang.binding.version} org.opendaylight.yangtools @@ -535,7 +608,7 @@ org.opendaylight.mdsal yang-binding - ${odl.yangtools.version} + ${odl.mdsal.yang.binding.version} org.opendaylight.yangtools diff --git a/sli/common/pom.xml b/sli/common/pom.xml index 7a08c34..eb06e6b 100755 --- a/sli/common/pom.xml +++ b/sli/common/pom.xml @@ -44,7 +44,7 @@ org.opendaylight.mdsal yang-binding - ${odl.yangtools.version} + ${odl.mdsal.yang.binding.version} org.opendaylight.yangtools diff --git a/sli/common/src/main/java/org/openecomp/sdnc/sli/BreakNodeException.java b/sli/common/src/main/java/org/openecomp/sdnc/sli/BreakNodeException.java new file mode 100644 index 0000000..3e355ba --- /dev/null +++ b/sli/common/src/main/java/org/openecomp/sdnc/sli/BreakNodeException.java @@ -0,0 +1,46 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli; + +public class BreakNodeException extends SvcLogicException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public BreakNodeException() + { + super(); + } + + public BreakNodeException(String message) + { + super(message); + } + + public BreakNodeException(String message, Throwable t) + { + super(message, t); + } + +} diff --git a/sli/common/src/main/java/org/openecomp/sdnc/sli/MessageWriter.java b/sli/common/src/main/java/org/openecomp/sdnc/sli/MessageWriter.java new file mode 100644 index 0000000..5e5b621 --- /dev/null +++ b/sli/common/src/main/java/org/openecomp/sdnc/sli/MessageWriter.java @@ -0,0 +1,302 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli; + +import java.io.File; +import java.io.FileInputStream; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.Properties; + +import javax.sql.rowset.CachedRowSet; + +import org.openecomp.sdnc.sli.resource.dblib.DbLibService; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class MessageWriter { + + private static final Logger LOG = LoggerFactory.getLogger(MessageWriter.class); + + private static final String DBLIB_SERVICE = "org.openecomp.sdnc.sli.resource.dblib.DBResourceManager"; + private static final String SVCLOGIC_PROP_VAR = "SDNC_SLI_PROPERTIES"; + private static final String SDNC_CONFIG_DIR = "SDNC_CONFIG_DIR"; + + private static final String INCOMING_PROPERTY_NAME = "org.openecomp.sdnc.sli.MessageWriter.writeIncomingRequests"; + private static final String OUTGOING_PROPERTY_NAME = "org.openecomp.sdnc.sli.MessageWriter.writeOutgoingRequests"; + + private static final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + private static DbLibService dbLibService = null; + + private static boolean incomingEnabled = false; + private static boolean outgoingEnabled = false; + + private static boolean initialized = false; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private static void init() { + if (initialized) + return; + + initialized = true; + + // Read properties + Properties props = new Properties(); + String propPath = System.getenv(SVCLOGIC_PROP_VAR); + + if (propPath == null) { + String propDir = System.getenv(SDNC_CONFIG_DIR); + if (propDir == null) { + propDir = "/opt/sdnc/data/properties"; + } + propPath = propDir + "/svclogic.properties"; + LOG.warn("Environment variable " + SVCLOGIC_PROP_VAR + " unset - defaulting to " + propPath); + } + + File propFile = new File(propPath); + + if (!propFile.exists()) { + LOG.warn("Property file does not exist: " + propPath); + } + + try { + props.load(new FileInputStream(propFile)); + } catch (Exception e) { + LOG.warn("Error loading property file: " + propPath, e); + } + + incomingEnabled = Boolean.valueOf(props.getProperty(INCOMING_PROPERTY_NAME, "false")); + outgoingEnabled = Boolean.valueOf(props.getProperty(OUTGOING_PROPERTY_NAME, "false")); + + LOG.info(INCOMING_PROPERTY_NAME + ": " + incomingEnabled); + LOG.info(OUTGOING_PROPERTY_NAME + ": " + outgoingEnabled); + + if (dbLibService != null) + return; + + BundleContext bctx = FrameworkUtil.getBundle(MessageWriter.class).getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(DBLIB_SERVICE); + + if (sref == null) { + LOG.warn("Could not find service reference for DBLIB service (" + DBLIB_SERVICE + ")"); + } else { + dbLibService = (DbLibService) bctx.getService(sref); + if (dbLibService == null) { + LOG.warn("Could not find service reference for DBLIB service (" + DBLIB_SERVICE + ")"); + } + } + } + + public static void saveOutgoingRequest( + String requestId, + String serviceInstanceId, + String targetUrl, + String request) { + try { + init(); + + if (!outgoingEnabled) + return; + + if (serviceInstanceId == null || serviceInstanceId.trim().length() == 0) + serviceInstanceId = "NA"; + + int seqnum = getLastSequenceNumber("OUTGOING_MESSAGE", requestId) + 1; + String now = df.format(new Date()); + + String sql = "INSERT INTO OUTGOING_MESSAGE (\n" + + " request_id, sequence_number, service_instance_id, target_url, request, start_time)\n" + + "VALUES (?, ?, ?, ?, ?, ?)"; + + ArrayList data = new ArrayList<>(); + data.add(requestId); + data.add(String.valueOf(seqnum)); + data.add(serviceInstanceId); + data.add(targetUrl); + data.add(request); + data.add(now); + + dbLibService.writeData(sql, data, null); + + } catch (Exception e) { + LOG.warn("Failed to save outgoing request for request-id: " + requestId, e); + } + } + + public static void saveOutgoingResponse(String requestId, int httpResponseCode, String response) { + try { + init(); + + if (!outgoingEnabled) + return; + + int seqnum = getLastSequenceNumber("OUTGOING_MESSAGE", requestId); + if (seqnum == 0) { + LOG.warn("Failed to save outgoing response for request-id: " + requestId + + ": Request record not found in OUTGOING_MESSAGE"); + return; + } + + String now = df.format(new Date()); + + String sql = "UPDATE OUTGOING_MESSAGE SET http_response_code = ?, response = ?,\n" + + " duration = timestampdiff(MICROSECOND, start_time, ?) / 1000\n" + + "WHERE request_id = ? AND sequence_number = ?"; + + ArrayList data = new ArrayList<>(); + data.add(String.valueOf(httpResponseCode)); + data.add(response); + data.add(now); + data.add(requestId); + data.add(String.valueOf(seqnum)); + + dbLibService.writeData(sql, data, null); + + } catch (Exception e) { + LOG.warn("Failed to save outgoing response for request-id: " + requestId, e); + } + } + + public static void saveIncomingRequest( + String requestId, + String serviceInstanceId, + String requestHost, + String request) { + try { + init(); + + if (!incomingEnabled) + return; + + if (serviceInstanceId == null || serviceInstanceId.trim().length() == 0) + serviceInstanceId = "NA"; + + int seqnum = getLastSequenceNumber("INCOMING_MESSAGE", requestId) + 1; + String now = df.format(new Date()); + + String sql = "INSERT INTO INCOMING_MESSAGE (\n" + + " request_id, sequence_number, service_instance_id, request_host, request, start_time)\n" + + "VALUES (?, ?, ?, ?, ?, ?)"; + + ArrayList data = new ArrayList<>(); + data.add(requestId); + data.add(String.valueOf(seqnum)); + data.add(serviceInstanceId); + data.add(requestHost); + data.add(request); + data.add(now); + + dbLibService.writeData(sql, data, null); + + } catch (Exception e) { + LOG.warn("Failed to save incoming request for request-id: " + requestId, e); + } + } + + public static void saveIncomingResponse(String requestId, int httpResponseCode, String response) { + try { + init(); + + if (!incomingEnabled) + return; + + int seqnum = getLastSequenceNumber("INCOMING_MESSAGE", requestId); + if (seqnum == 0) { + LOG.warn("Failed to save response for request-id: " + requestId + + ": Request record not found in INCOMING_MESSAGE"); + return; + } + + String now = df.format(new Date()); + + String sql = "UPDATE INCOMING_MESSAGE SET http_response_code = ?, response = ?,\n" + + " duration = timestampdiff(MICROSECOND, start_time, ?) / 1000\n" + + "WHERE request_id = ? AND sequence_number = ?"; + + ArrayList data = new ArrayList<>(); + data.add(String.valueOf(httpResponseCode)); + data.add(response); + data.add(now); + data.add(requestId); + data.add(String.valueOf(seqnum)); + + dbLibService.writeData(sql, data, null); + + } catch (Exception e) { + LOG.warn("Failed to save response for request-id: " + requestId, e); + } + } + + public static String getServiceInstanceId(String requestId) throws SQLException { + init(); + + String sql = "SELECT service_instance_id FROM OUTGOING_MESSAGE WHERE request_id = '" + requestId + + "' ORDER BY sequence_number DESC"; + + CachedRowSet rs = null; + try { + rs = dbLibService.getData(sql, null, null); + if (rs.next()) { + return rs.getString("service_instance_id"); + } + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception e) { + LOG.warn("Failed to close CachedRowSet", e); + } + } + } + return null; + } + + private static int getLastSequenceNumber(String tableName, String requestId) throws SQLException { + String sql = "SELECT sequence_number FROM " + tableName + " WHERE request_id = '" + requestId + + "' ORDER BY sequence_number DESC"; + + CachedRowSet rs = null; + try { + rs = dbLibService.getData(sql, null, null); + if (rs.next()) { + return rs.getInt("sequence_number"); + } + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception e) { + LOG.warn("Failed to close CachedRowSet", e); + } + } + } + return 0; + } +} diff --git a/sli/common/src/main/java/org/openecomp/sdnc/sli/SvcLogicExprListener.java b/sli/common/src/main/java/org/openecomp/sdnc/sli/SvcLogicExprListener.java index d8d3384..4fef12f 100644 --- a/sli/common/src/main/java/org/openecomp/sdnc/sli/SvcLogicExprListener.java +++ b/sli/common/src/main/java/org/openecomp/sdnc/sli/SvcLogicExprListener.java @@ -77,7 +77,7 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener private void pushExpr(SvcLogicExpression expr) { - LOG.debug("Pushing expression ["+expr.getClass().getName()+"]"); + LOG.trace("Pushing expression ["+expr.getClass().getName()+"]"); if (curExpr != null) { exprStack.push(curExpr); @@ -89,7 +89,7 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener { if (exprStack.isEmpty()) { - LOG.debug("Popping last expression"); + LOG.trace("Popping last expression"); topExpr = curExpr; } else @@ -97,7 +97,7 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener SvcLogicExpression lastExpr = curExpr; curExpr = exprStack.pop(); curExpr.addOperand(lastExpr); - LOG.debug("New curExpr is ["+curExpr.getClass().getName()+"]"); + LOG.trace("New curExpr is ["+curExpr.getClass().getName()+"]"); } } @@ -107,7 +107,7 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener String atomText = ctx.getText(); - LOG.debug("enterAtom: text = "+atomText); + LOG.trace("enterAtom: text = "+atomText); SvcLogicAtom newAtom = new SvcLogicAtom(atomText); @@ -118,7 +118,7 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener @Override public void enterMultExpr(MultExprContext ctx) { - LOG.debug("enterMultExpr: text = "+ctx.getText()); + LOG.trace("enterMultExpr: text = "+ctx.getText()); SvcLogicBinaryExpression curBinExpr = new SvcLogicBinaryExpression(); pushExpr(curBinExpr); @@ -127,7 +127,7 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener for (TerminalNode nd : opList) { - LOG.debug("enterMultExpr: operator - "+nd.getText()); + LOG.trace("enterMultExpr: operator - "+nd.getText()); curBinExpr.addOperator(nd.getText()); } @@ -136,7 +136,7 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener @Override public void exitMultExpr(MultExprContext ctx) { - LOG.debug("exitMultExpr: text = "+ctx.getText()); + LOG.trace("exitMultExpr: text = "+ctx.getText()); popExpr(); @@ -144,13 +144,13 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener @Override public void exitAtom(AtomContext ctx) { - LOG.debug("exitAtom: text = "+ctx.getText()); + LOG.trace("exitAtom: text = "+ctx.getText()); popExpr(); } @Override public void enterAddExpr(AddExprContext ctx) { - LOG.debug("enterAddExpr: text = "+ctx.getText()); + LOG.trace("enterAddExpr: text = "+ctx.getText()); List opList = ctx.ADDOP(); @@ -160,7 +160,7 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener for (TerminalNode nd : opList) { - LOG.debug("enterAddExpr: operator - "+nd.getText()); + LOG.trace("enterAddExpr: operator - "+nd.getText()); curBinExpr.addOperator(nd.getText()); } @@ -168,19 +168,19 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener @Override public void exitAddExpr(AddExprContext ctx) { - LOG.debug("exitAddExpr: text = "+ctx.getText()); + LOG.trace("exitAddExpr: text = "+ctx.getText()); popExpr(); } @Override public void enterFuncExpr(FuncExprContext ctx) { - LOG.debug("enterFuncExpr: text = "+ctx.getText()); - LOG.debug("enterFuncExpr - IDENTIFIER : "+ctx.IDENTIFIER().getText()); + LOG.trace("enterFuncExpr: text = "+ctx.getText()); + LOG.trace("enterFuncExpr - IDENTIFIER : "+ctx.IDENTIFIER().getText()); for (ExprContext expr: ctx.expr()) { - LOG.debug("enterFuncExpr - expr = "+expr.getText()); + LOG.trace("enterFuncExpr - expr = "+expr.getText()); } @@ -189,25 +189,25 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener @Override public void exitFuncExpr(FuncExprContext ctx) { - LOG.debug("exitFuncExpr: text = "+ctx.getText()); + LOG.trace("exitFuncExpr: text = "+ctx.getText()); popExpr(); } @Override public void enterParenExpr(ParenExprContext ctx) { - LOG.debug("enterParenExpr: text = "+ctx.getText()); - LOG.debug("enterParenExpr: expr = "+ctx.expr().getText()); + LOG.trace("enterParenExpr: text = "+ctx.getText()); + LOG.trace("enterParenExpr: expr = "+ctx.expr().getText()); } @Override public void exitParenExpr(ParenExprContext ctx) { - LOG.debug("exitParenExpr: text = "+ctx.getText()); + LOG.trace("exitParenExpr: text = "+ctx.getText()); } @Override public void enterRelExpr(RelExprContext ctx) { - LOG.debug("enterRelExpr: text = "+ctx.getText()); + LOG.trace("enterRelExpr: text = "+ctx.getText()); List opList = ctx.RELOP(); @@ -218,7 +218,7 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener for (TerminalNode nd : opList) { - LOG.debug("enterRelExpr: operator - "+nd.getText()); + LOG.trace("enterRelExpr: operator - "+nd.getText()); curBinExpr.addOperator(nd.getText()); } @@ -226,28 +226,28 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener @Override public void exitRelExpr(RelExprContext ctx) { - LOG.debug("exitRelExpr: text = "+ctx.getText()); + LOG.trace("exitRelExpr: text = "+ctx.getText()); popExpr(); } @Override public void enterCompareExpr(CompareExprContext ctx) { - LOG.debug("enterCompareExpr: text = "+ctx.getText()); + LOG.trace("enterCompareExpr: text = "+ctx.getText()); TerminalNode nd = ctx.COMPAREOP(); SvcLogicBinaryExpression curBinExpr = new SvcLogicBinaryExpression(); pushExpr(curBinExpr); - LOG.debug("enterCompareExpr: operator - "+nd.getText()); + LOG.trace("enterCompareExpr: operator - "+nd.getText()); curBinExpr.addOperator(nd.getText()); } @Override public void exitCompareExpr(CompareExprContext ctx) { - LOG.debug("exitCompareExpr : text = "+ctx.getText()); + LOG.trace("exitCompareExpr : text = "+ctx.getText()); popExpr(); } @@ -256,18 +256,18 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener @Override public void enterConstant(ConstantContext ctx) { - LOG.debug("enterConstant: text = "+ctx.getText()); + LOG.trace("enterConstant: text = "+ctx.getText()); } @Override public void exitConstant(ConstantContext ctx) { - LOG.debug("exitConstant: text = "+ctx.getText()); + LOG.trace("exitConstant: text = "+ctx.getText()); } @Override public void enterVariable(VariableContext ctx) { - LOG.debug("enterVariable: text = "+ctx.getText()); + LOG.trace("enterVariable: text = "+ctx.getText()); } @@ -290,12 +290,12 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener @Override public void exitVariableLead(VariableLeadContext ctx) { - LOG.debug("exitVariableLead: text ="+ctx.getText()); + LOG.trace("exitVariableLead: text ="+ctx.getText()); } @Override public void enterVariableTerm(VariableTermContext ctx) { - LOG.debug("enterVariableTerm: text ="+ctx.getText()); + LOG.trace("enterVariableTerm: text ="+ctx.getText()); String name = ctx.getText(); @@ -310,7 +310,7 @@ public class SvcLogicExprListener extends ExprGrammarBaseListener @Override public void exitVariableTerm(VariableTermContext ctx) { - LOG.debug("exitVariableTerm: text="+ctx.getText()); + LOG.trace("exitVariableTerm: text="+ctx.getText()); popExpr(); } } diff --git a/sli/common/src/main/java/org/openecomp/sdnc/sli/SvcLogicExpressionFactory.java b/sli/common/src/main/java/org/openecomp/sdnc/sli/SvcLogicExpressionFactory.java index 751564b..cce8e04 100644 --- a/sli/common/src/main/java/org/openecomp/sdnc/sli/SvcLogicExpressionFactory.java +++ b/sli/common/src/main/java/org/openecomp/sdnc/sli/SvcLogicExpressionFactory.java @@ -42,7 +42,7 @@ public class SvcLogicExpressionFactory { public static SvcLogicExpression parse(String exprStr) throws IOException { - LOG.debug("parse("+exprStr+")"); + LOG.trace("parse("+exprStr+")"); InputStream exprStream = new ByteArrayInputStream(exprStr.getBytes()); CharStream input = new ANTLRInputStream(exprStream); ExprGrammarLexer lexer = new ExprGrammarLexer(input); diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/BreakNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/BreakNodeExecutor.java new file mode 100644 index 0000000..0f8719c --- /dev/null +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/BreakNodeExecutor.java @@ -0,0 +1,42 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.provider; + +import org.openecomp.sdnc.sli.BreakNodeException; +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicException; +import org.openecomp.sdnc.sli.SvcLogicNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BreakNodeExecutor extends SvcLogicNodeExecutor { + + private static final Logger LOG = LoggerFactory.getLogger(BreakNodeExecutor.class); + + @Override + public SvcLogicNode execute(SvcLogicServiceImpl svc, SvcLogicNode node, SvcLogicContext ctx) throws SvcLogicException { + String message = "BreakNodeExecutor encountered break with nodeId " + node.getNodeId(); + LOG.debug(message); + throw new BreakNodeException(message); + } + +} diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/CallNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/CallNodeExecutor.java index 2ce4f0a..da0bc56 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/CallNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/CallNodeExecutor.java @@ -114,24 +114,21 @@ public class CallNodeExecutor extends SvcLogicNodeExecutor { version = SvcLogicExpressionResolver.evaluate(moduleExpr, node, ctx); } + String parentGraph = ctx.getAttribute("currentGraph"); + ctx.setAttribute("parentGraph", parentGraph); SvcLogicStore store = SvcLogicActivator.getStore(); - LOG.debug("Calling ["+module+","+rpc+","+version+","+mode+"]"); - - if (store != null) - { + if (store != null) { SvcLogicGraph calledGraph = store.fetch(module, rpc, version, mode); - - if (calledGraph != null) - { + LOG.debug("Parent " + parentGraph + " is calling child " + calledGraph.toString()); + ctx.setAttribute("currentGraph", calledGraph.toString()); + if (calledGraph != null) { svc.execute(calledGraph, ctx); outValue = ctx.getStatus(); - } - else - { - LOG.debug("Could not find service logic for ["+module+","+rpc+","+version+","+mode+"]"); + } else { + LOG.error("Could not find service logic for [" + module + "," + rpc + "," + version + "," + mode + "]"); } } else @@ -144,6 +141,7 @@ public class CallNodeExecutor extends SvcLogicNodeExecutor { if (LOG.isDebugEnabled()) { LOG.debug("about to execute " + outValue + " branch"); } + ctx.setAttribute("currentGraph", parentGraph); return (nextNode); } @@ -157,6 +155,9 @@ public class CallNodeExecutor extends SvcLogicNodeExecutor { LOG.debug("no " + outValue + " or Other branch found"); } } + ctx.setAttribute("currentGraph", parentGraph); + ctx.setAttribute("parentGraph", null); + return (nextNode); } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/DeleteNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/DeleteNodeExecutor.java index 97da2a6..375c631 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/DeleteNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/DeleteNodeExecutor.java @@ -53,15 +53,8 @@ public class DeleteNodeExecutor extends SvcLogicNodeExecutor { + plugin); } - BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) - .getBundleContext(); - - ServiceReference sref = bctx.getServiceReference(plugin); - - if (sref != null) { - SvcLogicResource resourcePlugin = (SvcLogicResource) bctx - .getService(sref); + SvcLogicResource resourcePlugin = getSvcLogicResource(plugin); if (resourcePlugin != null) { try { @@ -85,10 +78,7 @@ public class DeleteNodeExecutor extends SvcLogicNodeExecutor { LOG.warn("Could not find SvcLogicResource object for plugin " + plugin); } - } else { - LOG.warn("Could not find service reference object for plugin " - + plugin); - } + SvcLogicNode nextNode = node.getOutcomeValue(outValue); if (nextNode != null) { @@ -110,6 +100,22 @@ public class DeleteNodeExecutor extends SvcLogicNodeExecutor { } return (nextNode); } + + protected SvcLogicResource getSvcLogicResource(String plugin) { + BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) + .getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(plugin); + if (sref != null) { + SvcLogicResource resourcePlugin = (SvcLogicResource) bctx + .getService(sref); + return resourcePlugin; + } + else { + LOG.warn("Could not find service reference object for plugin " + plugin); + return null; + } + } } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ExecuteNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ExecuteNodeExecutor.java index 158c843..85aede7 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ExecuteNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ExecuteNodeExecutor.java @@ -21,6 +21,7 @@ package org.openecomp.sdnc.sli.provider; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; @@ -44,6 +45,7 @@ public class ExecuteNodeExecutor extends SvcLogicNodeExecutor { private static final Logger LOG = LoggerFactory .getLogger(ExecuteNodeExecutor.class); + private static final String pluginErrorMessage = "Could not execute plugin. SvcLogic status will be set to failure."; public SvcLogicNode execute(SvcLogicServiceImpl svc, SvcLogicNode node, SvcLogicContext ctx) throws SvcLogicException { @@ -55,35 +57,30 @@ public class ExecuteNodeExecutor extends SvcLogicNodeExecutor { LOG.debug("execute node encountered - looking for plugin " + pluginName); } - - BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) - .getBundleContext(); - ServiceReference sref = bctx.getServiceReference(pluginName); + SvcLogicJavaPlugin plugin = getSvcLogicJavaPlugin(pluginName); - if (sref == null) { + if (plugin == null) { outValue = "not-found"; } else { - SvcLogicJavaPlugin plugin = (SvcLogicJavaPlugin) bctx - .getService(sref); - - String methodName = SvcLogicExpressionResolver.evaluate(node.getAttribute("method"), node, ctx); - + + String methodName = evaluate(node.getAttribute("method"), node, ctx); + Class pluginClass = plugin.getClass(); - + Method pluginMethod = null; - + try { pluginMethod = pluginClass.getMethod(methodName, Map.class, SvcLogicContext.class); - } catch (Exception e) { - LOG.error("Caught exception looking for method "+pluginName+"."+methodName+"(Map, SvcLogicContext)"); + } catch (NoSuchMethodException e) { + LOG.error(pluginErrorMessage, e); } - + if (pluginMethod == null) { outValue = "unsupported-method"; } else { try { - + Map parmMap = new HashMap(); Set> parmSet = node @@ -95,21 +92,34 @@ public class ExecuteNodeExecutor extends SvcLogicNodeExecutor { String curName = curEnt.getKey(); SvcLogicExpression curExpr = curEnt.getValue(); String curExprValue = SvcLogicExpressionResolver.evaluate(curExpr, node, ctx); - + LOG.debug("Parameter "+curName+" = "+curExpr.asParsedExpr()+" resolves to "+curExprValue); parmMap.put(curName,curExprValue); } - - pluginMethod.invoke(plugin, parmMap, ctx); - - outValue = "success"; - } catch (Exception e) { - LOG.error("Caught exception executing "+pluginName+"."+methodName, e); - + + Object o = pluginMethod.invoke(plugin, parmMap, ctx); + String emitsOutcome = SvcLogicExpressionResolver.evaluate(node.getAttribute("emitsOutcome"), node, ctx); + + outValue = mapOutcome(o, emitsOutcome); + + } catch (InvocationTargetException e) { + if(e.getCause() != null){ + LOG.error(pluginErrorMessage, e.getCause()); + }else{ + LOG.error(pluginErrorMessage, e); + } outValue = "failure"; ctx.setStatus("failure"); - } + } catch (IllegalAccessException e) { + LOG.error(pluginErrorMessage, e); + outValue = "failure"; + ctx.setStatus("failure"); + } catch (IllegalArgumentException e) { + LOG.error(pluginErrorMessage, e); + outValue = "failure"; + ctx.setStatus("failure"); + } } } @@ -135,4 +145,37 @@ public class ExecuteNodeExecutor extends SvcLogicNodeExecutor { return (nextNode); } + protected SvcLogicJavaPlugin getSvcLogicJavaPlugin(String pluginName){ + BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) + .getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(pluginName); + + if (sref == null) { + LOG.warn("Could not find service reference object for plugin " + pluginName); + return null; + } else { + SvcLogicJavaPlugin plugin = (SvcLogicJavaPlugin) bctx + .getService(sref); + return plugin; + } + } + protected String evaluate(SvcLogicExpression expr, SvcLogicNode node, SvcLogicContext ctx) throws SvcLogicException { + return SvcLogicExpressionResolver.evaluate(node.getAttribute("method"), node, ctx); + } + + public String mapOutcome(Object o, String emitsOutcome) { + if (emitsOutcome != null) { + Boolean nodeEmitsOutcome = Boolean.valueOf(emitsOutcome); + if (nodeEmitsOutcome) { + return (String) o; + } else { + return "success"; + } + + } else { + return "success"; + } + } + } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ExistsNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ExistsNodeExecutor.java index 48b511e..464c676 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ExistsNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ExistsNodeExecutor.java @@ -55,14 +55,9 @@ public class ExistsNodeExecutor extends SvcLogicNodeExecutor { + plugin); } - BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) - .getBundleContext(); - ServiceReference sref = bctx.getServiceReference(plugin); - if (sref != null) { - SvcLogicResource resourcePlugin = (SvcLogicResource) bctx - .getService(sref); + SvcLogicResource resourcePlugin = getSvcLogicResource(plugin); if (resourcePlugin != null) { @@ -87,10 +82,6 @@ public class ExistsNodeExecutor extends SvcLogicNodeExecutor { LOG.warn("Could not find SvcLogicResource object for plugin " + plugin); } - } else { - LOG.warn("Could not find service reference object for plugin " - + plugin); - } SvcLogicNode nextNode = node.getOutcomeValue(outValue); if (nextNode != null) { @@ -114,5 +105,21 @@ public class ExistsNodeExecutor extends SvcLogicNodeExecutor { return (nextNode); } + protected SvcLogicResource getSvcLogicResource(String plugin) { + BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) + .getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(plugin); + if (sref != null) { + SvcLogicResource resourcePlugin = (SvcLogicResource) bctx + .getService(sref); + return resourcePlugin; + } + else { + LOG.warn("Could not find service reference object for plugin " + plugin); + return null; + } + } + } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ForNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ForNodeExecutor.java index a669712..e9fdc55 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ForNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ForNodeExecutor.java @@ -21,6 +21,7 @@ package org.openecomp.sdnc.sli.provider; +import org.openecomp.sdnc.sli.BreakNodeException; import org.openecomp.sdnc.sli.SvcLogicContext; import org.openecomp.sdnc.sli.SvcLogicException; import org.openecomp.sdnc.sli.SvcLogicExpression; @@ -40,7 +41,7 @@ public class ForNodeExecutor extends SvcLogicNodeExecutor { SvcLogicExpression atomicExpr = node.getAttribute("atomic"); String atomicStr = SvcLogicExpressionResolver.evaluate(atomicExpr, node, ctx); boolean isAtomic = !("false".equalsIgnoreCase(atomicStr)); - + int numOutcomes = node.getNumOutcomes(); String idxVar = SvcLogicExpressionResolver.evaluate( node.getAttribute("index"), node, ctx); @@ -59,21 +60,30 @@ public class ForNodeExecutor extends SvcLogicNodeExecutor { startIdx = Integer.parseInt(startVal); endIdx = Integer.parseInt(endVal); } catch (NumberFormatException e) { - throw new SvcLogicException("Invalid index values [" + startVal - + "," + endVal + "]"); + SvcLogicExpression silentFailureExpr = node.getAttribute("silentFailure"); + String silentFailure = SvcLogicExpressionResolver.evaluate(silentFailureExpr, node, ctx); + boolean isSilentFailure = Boolean.parseBoolean(silentFailure); + String message = "Invalid index values [" + startVal + "," + endVal + "]"; + if(!isSilentFailure){ + throw new SvcLogicException(message); + }else{ + LOG.debug(message + ". Not exiting because silentFailure was set to true."); + return(null); + } } + try { for (int ctr = startIdx; ctr < endIdx; ctr++) { ctx.setAttribute(idxVar, "" + ctr); for (int i = 0; i < numOutcomes; i++) { - + if ("failure".equals(ctx.getStatus()) && isAtomic) { LOG.info("For - stopped executing nodes due to failure status"); return(null); } - + SvcLogicNode nextNode = node.getOutcomeValue("" + (i + 1)); if (nextNode != null) { if (LOG.isDebugEnabled()) { @@ -83,7 +93,6 @@ public class ForNodeExecutor extends SvcLogicNodeExecutor { while (innerNextNode != null) { innerNextNode = svc.executeNode(innerNextNode, ctx); } - } else { if (LOG.isDebugEnabled()) { LOG.debug("For - done: no outcome " + (i + 1)); @@ -91,6 +100,9 @@ public class ForNodeExecutor extends SvcLogicNodeExecutor { } } } + } catch (BreakNodeException br) { + LOG.debug("ForNodeExecutor caught break"); + } return (null); } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/GetResourceNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/GetResourceNodeExecutor.java index ce0fe68..c260db2 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/GetResourceNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/GetResourceNodeExecutor.java @@ -84,14 +84,8 @@ public class GetResourceNodeExecutor extends SvcLogicNodeExecutor { + plugin); } - BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) - .getBundleContext(); - ServiceReference sref = bctx.getServiceReference(plugin); - - if (sref != null) { - SvcLogicResource resourcePlugin = (SvcLogicResource) bctx - .getService(sref); + SvcLogicResource resourcePlugin = getSvcLogicResource(plugin); if (resourcePlugin != null) { @@ -116,9 +110,7 @@ public class GetResourceNodeExecutor extends SvcLogicNodeExecutor { LOG.warn("Could not find SvcLogicResource object for plugin " + plugin); } - } else { - LOG.warn("Cound not find service reference for plugin " + plugin); - } + SvcLogicNode nextNode = node.getOutcomeValue(outValue); if (nextNode != null) { @@ -141,6 +133,22 @@ public class GetResourceNodeExecutor extends SvcLogicNodeExecutor { } return (nextNode); } + + protected SvcLogicResource getSvcLogicResource(String plugin) { + BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) + .getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(plugin); + if (sref != null) { + SvcLogicResource resourcePlugin = (SvcLogicResource) bctx + .getService(sref); + return resourcePlugin; + } + else { + LOG.warn("Could not find service reference object for plugin " + plugin); + return null; + } + } } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/IsAvailableNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/IsAvailableNodeExecutor.java index 47eebe4..00c7e66 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/IsAvailableNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/IsAvailableNodeExecutor.java @@ -56,14 +56,8 @@ public class IsAvailableNodeExecutor extends SvcLogicNodeExecutor { + plugin); } - BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) - .getBundleContext(); + SvcLogicResource resourcePlugin = getSvcLogicResource(plugin); - ServiceReference sref = bctx.getServiceReference(plugin); - - if (sref != null) { - SvcLogicResource resourcePlugin = (SvcLogicResource) bctx - .getService(sref); if (resourcePlugin != null) { try { @@ -86,10 +80,6 @@ public class IsAvailableNodeExecutor extends SvcLogicNodeExecutor { LOG.warn("Could not find SvcLogicResource object for plugin " + plugin); } - } else { - LOG.warn("Could not find service reference object for plugin " - + plugin); - } SvcLogicNode nextNode = node.getOutcomeValue(outValue); if (nextNode != null) { @@ -113,5 +103,20 @@ public class IsAvailableNodeExecutor extends SvcLogicNodeExecutor { return (nextNode); } + protected SvcLogicResource getSvcLogicResource(String plugin) { + BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) + .getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(plugin); + if (sref != null) { + SvcLogicResource resourcePlugin = (SvcLogicResource) bctx + .getService(sref); + return resourcePlugin; + } + else { + LOG.warn("Could not find service reference object for plugin " + plugin); + return null; + } + } } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/MdsalHelper.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/MdsalHelper.java index 9847416..4c5ee06 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/MdsalHelper.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/MdsalHelper.java @@ -47,1359 +47,1140 @@ import org.slf4j.LoggerFactory; public class MdsalHelper { - private static final Logger LOG = LoggerFactory.getLogger(MdsalHelper.class); - public static final String PROPERTIES_FILE="/opt/bvc/controller/configuration/l3sdn.properties"; - private static Properties properties = new Properties(); - - - public static void setProperties(Properties properties) { - - for (Object propNameObj: properties.keySet()) { - String propName = (String) propNameObj; - MdsalHelper.properties.setProperty(propName, properties.getProperty(propName)); + private static final Logger LOG = LoggerFactory.getLogger(MdsalHelper.class); + private static Properties yangMappingProperties = new Properties(); + + @Deprecated + public static void setProperties(Properties input) { + setYangMappingProperties(input); + } + + public static void setYangMappingProperties(Properties properties) { + for (Object propNameObj : properties.keySet()) { + String propName = (String) propNameObj; + MdsalHelper.yangMappingProperties.setProperty(propName, properties.getProperty(propName)); + } + } + + public static void loadProperties(String propertiesFile) { + File file = new File(propertiesFile); + Properties properties = new Properties(); + InputStream input = null; + if (file.isFile() && file.canRead()) { + try { + input = new FileInputStream(file); + properties.load(input); + MdsalHelper.setYangMappingProperties(properties); + LOG.info("Loaded properties from " + propertiesFile); + } catch (Exception e) { + LOG.error("Failed to load properties " + propertiesFile + "\n", e); + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + LOG.error("Failed to close properties file " + propertiesFile + "\n", e); + } } + } + }else{ + LOG.error("Failed to load the properties file " + propertiesFile + "\n"); + LOG.error("Either isFile or canRead returned false for " + propertiesFile + "\n"); } + } - public static void loadProperties() { - - File file = new File(PROPERTIES_FILE); - Properties properties = new Properties(); - InputStream input = null; - if (file.isFile() && file.canRead()) { - try { - input = new FileInputStream(file); - properties.load(input); - MdsalHelper.setProperties(properties); - LOG.info("Loaded properties from " + PROPERTIES_FILE ); - } catch (Exception e) { - LOG.error("Failed to load properties " + PROPERTIES_FILE +"\n",e); - } finally { - if (input != null) { - try { - input.close(); - } catch (IOException e) { - LOG.error("Failed to close properties file " + PROPERTIES_FILE +"\n",e); - } - } - } - } + public static Properties toProperties(Properties props, Object fromObj) { + Class fromClass = null; + + if (fromObj != null) { + fromClass = fromObj.getClass(); } - - public static Properties toProperties(Properties props, Object fromObj) { - Class fromClass = null; - - if (fromObj != null) - { - fromClass = fromObj.getClass(); - } - return (toProperties(props, "", fromObj, fromClass)); + return (toProperties(props, "", fromObj, fromClass)); + } + + public static Properties toProperties(Properties props, String pfx, Object fromObj) { + Class fromClass = null; + + if (fromObj != null) { + fromClass = fromObj.getClass(); } - - public static Properties toProperties(Properties props, String pfx, Object fromObj) - { - Class fromClass = null; - - if (fromObj != null) - { - fromClass = fromObj.getClass(); - } - - return(toProperties(props, pfx, fromObj, fromClass)); + + return (toProperties(props, pfx, fromObj, fromClass)); + } + + public static Properties toProperties(Properties props, String pfx, Object fromObj, Class fromClass) { + + if (fromObj == null) { + return (props); } - public static Properties toProperties(Properties props, String pfx, - Object fromObj, Class fromClass) { + String simpleName = fromClass.getSimpleName(); + + LOG.trace("Extracting properties from " + fromClass.getName() + " class"); + if (fromObj instanceof List) { + + // Class is a List. List should contain yang-generated classes. + LOG.trace(fromClass.getName() + " is a List"); + + List fromList = (List) fromObj; + + for (int i = 0; i < fromList.size(); i++) { + toProperties(props, pfx + "[" + i + "]", fromList.get(i), fromClass); + } + props.setProperty(pfx + "_length", "" + fromList.size()); - if (fromObj == null) { - return (props); + } else if (isYangGenerated(fromClass)) { + // Class is yang generated. + LOG.trace(fromClass.getName() + " is a Yang-generated class"); + + String propNamePfx = null; + + // If called from a list (so prefix ends in ']'), don't + // add class name again + if (pfx.endsWith("]")) { + propNamePfx = pfx; + } else { + if ((pfx != null) && (pfx.length() > 0)) { + propNamePfx = pfx; + } else { + propNamePfx = toLowerHyphen(fromClass.getSimpleName()); } - - - String simpleName = fromClass.getSimpleName(); - LOG.trace("Extracting properties from " + fromClass.getName() - + " class"); - if (fromObj instanceof List) { + if (propNamePfx.endsWith("-builder")) { + propNamePfx = propNamePfx.substring(0, propNamePfx.length() - "-builder".length()); + } - // Class is a List. List should contain yang-generated classes. - LOG.trace(fromClass.getName() + " is a List"); + if (propNamePfx.endsWith("-impl")) { + propNamePfx = propNamePfx.substring(0, propNamePfx.length() - "-impl".length()); + } + } - List fromList = (List) fromObj; + // Iterate through getter methods to figure out values we need to + // save from - for (int i = 0; i < fromList.size(); i++) { - toProperties(props, pfx + "[" + i + "]", fromList.get(i), fromClass); - } - props.setProperty(pfx + "_length", "" + fromList.size()); + int numGetters = 0; + String lastGetterName = null; + String propVal = null; - } else if (isYangGenerated(fromClass)) { - // Class is yang generated. - LOG.trace(fromClass.getName() + " is a Yang-generated class"); + for (Method m : fromClass.getMethods()) { + if (isGetter(m)) { - String propNamePfx = null; + numGetters++; + lastGetterName = m.getName(); - // If called from a list (so prefix ends in ']'), don't - // add class name again - if (pfx.endsWith("]")) { - propNamePfx = pfx; - } else { - if ((pfx != null) && (pfx.length() > 0)) { - propNamePfx = pfx ; - } else { - propNamePfx = toLowerHyphen(fromClass.getSimpleName()); - } + Class returnType = m.getReturnType(); + String fieldName; + if (m.getName().startsWith("get")) { + fieldName = toLowerHyphen(m.getName().substring(3)); + } else { - if (propNamePfx.endsWith("-builder")) { - propNamePfx = propNamePfx.substring(0, propNamePfx.length() - - "-builder".length()); + fieldName = toLowerHyphen(m.getName().substring(2)); + } + + fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); + + // Is the return type a yang generated class? + if (isYangGenerated(returnType)) { + // Is it an enum? + if (returnType.isEnum()) { + // Return type is a typedef. Save its value. + try { + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); } - if (propNamePfx.endsWith("-impl")) { - propNamePfx = propNamePfx.substring(0, propNamePfx.length() - - "-impl".length()); + Object retValue = m.invoke(fromObj); + + if (!isAccessible) { + m.setAccessible(isAccessible); + } + if (retValue != null) { + String propName = propNamePfx + "." + fieldName; + propVal = retValue.toString(); + props.setProperty(propName, mapEnumeratedValue(fieldName, propVal)); + } + } catch (Exception e) { + LOG.error("Caught exception trying to convert Yang-generated enum returned by " + fromClass.getName() + "." + m.getName() + "() to Properties entry", e); + } + } else if (isIpv4Address(returnType)) { + // Save its value + try { + String propName = propNamePfx + "." + fieldName; + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + Ipv4Address retValue = (Ipv4Address) m.invoke(fromObj); + if (!isAccessible) { + m.setAccessible(isAccessible); } - } - - // Iterate through getter methods to figure out values we need to - // save from - - int numGetters = 0; - String lastGetterName = null; - String propVal = null; - - for (Method m : fromClass.getMethods()) { - if (isGetter(m)) { - - numGetters++; - lastGetterName = m.getName(); - - Class returnType = m.getReturnType(); - String fieldName; - if (m.getName().startsWith("get")) { - fieldName = toLowerHyphen(m.getName().substring(3)); - } else { - fieldName = toLowerHyphen(m.getName().substring(2)); - } - - fieldName = fieldName.substring(0, 1).toLowerCase() - + fieldName.substring(1); - - // Is the return type a yang generated class? - if (isYangGenerated(returnType)) { - // Is it an enum? - if (returnType.isEnum()) { - // Return type is a typedef. Save its value. - try { - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - - Object retValue = m.invoke(fromObj); - - if (!isAccessible) { - m.setAccessible(isAccessible); - } - if (retValue != null) { - String propName = propNamePfx + "." - + fieldName; - propVal = retValue.toString(); - String yangProp = "yang." + fieldName + "." + propVal; - if ( properties.containsKey(yangProp)) { - propVal = properties.getProperty(yangProp); - LOG.trace("Adjusting property " + yangProp + " " + propVal); - } - LOG.debug("Setting property " + propName - + " to " + propVal); - props.setProperty(propName, propVal); - } - } catch (Exception e) { - LOG.error( - "Caught exception trying to convert Yang-generated enum returned by " - + fromClass.getName() + "." - + m.getName() - + "() to Properties entry", e); - } - } else if (isIpv4Address(returnType)) { - // Save its value - try { - String propName = propNamePfx + "." + fieldName; - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - Ipv4Address retValue = (Ipv4Address) m.invoke(fromObj); - if (!isAccessible) { - m.setAccessible(isAccessible); - } - - if (retValue != null) { - propVal = retValue.getValue().toString(); - LOG.debug("Setting property " + propName - + " to " + propVal); - props.setProperty(propName, propVal); - - } - } catch (Exception e) { - LOG.error( - "Caught exception trying to convert value returned by " - + fromClass.getName() + "." - + m.getName() - + "() to Properties entry", e); - } - } else if (isIpv6Address(returnType)) { - // Save its value - try { - String propName = propNamePfx + "." + fieldName; - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - Ipv6Address retValue = (Ipv6Address) m.invoke(fromObj); - if (!isAccessible) { - m.setAccessible(isAccessible); - } - - if (retValue != null) { - propVal = retValue.getValue().toString(); - LOG.debug("Setting property " + propName - + " to " + propVal); - props.setProperty(propName, propVal); - - } - } catch (Exception e) { - LOG.error( - "Caught exception trying to convert value returned by " - + fromClass.getName() + "." - + m.getName() - + "() to Properties entry", e); - } - } else if (isIpAddress(returnType)) { - // Save its value - try { - String propName = propNamePfx + "." + fieldName; - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - IpAddress retValue = (IpAddress) m.invoke(fromObj); - if (!isAccessible) { - m.setAccessible(isAccessible); - } - - if (retValue != null) { - propVal = new String(retValue.getValue()); - LOG.debug("Setting property " + propName - + " to " + propVal); - props.setProperty(propName, propVal); - - } - } catch (Exception e) { - LOG.error( - "Caught exception trying to convert value returned by " - + fromClass.getName() + "." - + m.getName() - + "() to Properties entry", e); - } - } else if (isIpPrefix(returnType)) { - // Save its value - try { - String propName = propNamePfx + "." + fieldName; - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - IpPrefix retValue = (IpPrefix) m.invoke(fromObj); - if (!isAccessible) { - m.setAccessible(isAccessible); - } - - if (retValue != null) { - propVal = new String(retValue.getValue()); - LOG.debug("Setting property " + propName - + " to " + propVal); - props.setProperty(propName, propVal); - - } - } catch (Exception e) { - LOG.error( - "Caught exception trying to convert value returned by " - + fromClass.getName() + "." - + m.getName() - + "() to Properties entry", e); - } - } else { - try { - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - Object retValue = m.invoke(fromObj); - - if (retValue instanceof byte[]) { - LOG.trace(m.getName()+" returns a byte[]"); - retValue = new String((byte[]) retValue, "UTF-8"); - LOG.trace("Converted byte array "+propNamePfx+"."+fieldName+"to string "+ retValue ); - } - if (!isAccessible) { - m.setAccessible(isAccessible); - } - if (retValue != null) { - toProperties(props, propNamePfx + "." + fieldName, retValue, returnType); - } - } catch (Exception e) { - - if (m.getName().equals("getKey")) { - LOG.trace("Caught "+e.getClass().getName()+" exception trying to convert results from getKey() - ignoring"); - } else { - LOG.error( - "Caught exception trying to convert Yang-generated class returned by" - + fromClass.getName() + "." - + m.getName() - + "() to Properties entry", e); - } - } - } - } else if (returnType.equals(Class.class)) { + if (retValue != null) { + propVal = retValue.getValue().toString(); + LOG.debug("Setting property " + propName + " to " + propVal); + props.setProperty(propName, propVal); - LOG.trace(m.getName() - + " returns a Class object - not interested"); + } + } catch (Exception e) { + LOG.error("Caught exception trying to convert value returned by " + fromClass.getName() + "." + m.getName() + "() to Properties entry", e); + } + } else if (isIpv6Address(returnType)) { + // Save its value + try { + String propName = propNamePfx + "." + fieldName; + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + Ipv6Address retValue = (Ipv6Address) m.invoke(fromObj); + if (!isAccessible) { + m.setAccessible(isAccessible); + } - } else if (List.class.isAssignableFrom(returnType)) { + if (retValue != null) { + propVal = retValue.getValue().toString(); + LOG.debug("Setting property " + propName + " to " + propVal); + props.setProperty(propName, propVal); - // This getter method returns a list. - try { - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - Object retList = m.invoke(fromObj); - if (!isAccessible) { - m.setAccessible(isAccessible); - } - // Figure out what type of elements are stored in this array. - Type paramType = m.getGenericReturnType(); - Type elementType = ((ParameterizedType) paramType) - .getActualTypeArguments()[0]; - toProperties(props, propNamePfx + "." + fieldName, - retList, (Class)elementType); - } catch (Exception e) { - LOG.error( - "Caught exception trying to convert List returned by " - + fromClass.getName() + "." - + m.getName() - + "() to Properties entry", e); - } + } + } catch (Exception e) { + LOG.error("Caught exception trying to convert value returned by " + fromClass.getName() + "." + m.getName() + "() to Properties entry", e); + } + } else if (isIpAddress(returnType)) { + // Save its value + try { + String propName = propNamePfx + "." + fieldName; + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + IpAddress retValue = (IpAddress) m.invoke(fromObj); + if (!isAccessible) { + m.setAccessible(isAccessible); + } - } else { + if (retValue != null) { + propVal = new String(retValue.getValue()); + LOG.debug("Setting property " + propName + " to " + propVal); + props.setProperty(propName, propVal); - // Method returns something that is not a List and not - // yang-generated. - // Save its value - try { - String propName = propNamePfx + "." + fieldName; - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - Object propValObj = m.invoke(fromObj); - if (!isAccessible) { - m.setAccessible(isAccessible); - } + } + } catch (Exception e) { + LOG.error("Caught exception trying to convert value returned by " + fromClass.getName() + "." + m.getName() + "() to Properties entry", e); + } + } else if (isIpPrefix(returnType)) { + // Save its value + try { + String propName = propNamePfx + "." + fieldName; + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + IpPrefix retValue = (IpPrefix) m.invoke(fromObj); + if (!isAccessible) { + m.setAccessible(isAccessible); + } - if (propValObj != null) { - if (propValObj instanceof byte[]) { - LOG.trace(m.getName()+" returns a byte[]"); - propVal = new String((byte[]) propValObj, "UTF-8"); - LOG.trace("Converted byte array "+propNamePfx+"."+fieldName+"to string "+ propVal ); + if (retValue != null) { + propVal = new String(retValue.getValue()); + LOG.debug("Setting property " + propName + " to " + propVal); + props.setProperty(propName, propVal); - } else { - propVal = propValObj.toString(); - } - LOG.debug("Setting property " + propName - + " to " + propVal); - props.setProperty(propName, propVal); + } + } catch (Exception e) { + LOG.error("Caught exception trying to convert value returned by " + fromClass.getName() + "." + m.getName() + "() to Properties entry", e); + } + } else { + try { + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + Object retValue = m.invoke(fromObj); - } - } catch (Exception e) { - if (m.getName().equals("getKey")) { - LOG.trace("Caught "+e.getClass().getName()+" exception trying to convert results from getKey() - ignoring"); - } else { - LOG.error( - "Caught exception trying to convert value returned by" - + fromClass.getName() + "." - + m.getName() - + "() to Properties entry", e); - } - } - } + if (retValue instanceof byte[]) { + LOG.trace(m.getName() + " returns a byte[]"); + retValue = new String((byte[]) retValue, "UTF-8"); + LOG.trace("Converted byte array " + propNamePfx + "." + fieldName + "to string " + retValue); + } + if (!isAccessible) { + m.setAccessible(isAccessible); + } + if (retValue != null) { + toProperties(props, propNamePfx + "." + fieldName, retValue, returnType); + } + } catch (Exception e) { + if (m.getName().equals("getKey")) { + LOG.trace("Caught " + e.getClass().getName() + " exception trying to convert results from getKey() - ignoring"); + } else { + LOG.error("Caught exception trying to convert Yang-generated class returned by" + fromClass.getName() + "." + m.getName() + "() to Properties entry", e); } + } } - - // End of method loop. If there was only one getter, named "getValue", then - // set value identified by "prefix" to that one value. - if ((numGetters == 1) && ("getValue".equals(lastGetterName))) { - LOG.trace("getValueFIX : "+ propNamePfx+" only has getValue() getter - setting "+propNamePfx+" = "+propVal); - props.setProperty(propNamePfx, propVal); - } else { - LOG.trace("getValueFIX : " + propNamePfx+" has "+numGetters+" getter(s), last one found was "+lastGetterName); - + } else if (returnType.equals(Class.class)) { + + LOG.trace(m.getName() + " returns a Class object - not interested"); + + } else if (List.class.isAssignableFrom(returnType)) { + + // This getter method returns a list. + try { + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + Object retList = m.invoke(fromObj); + if (!isAccessible) { + m.setAccessible(isAccessible); + } + // Figure out what type of elements are stored in + // this array. + Type paramType = m.getGenericReturnType(); + Type elementType = ((ParameterizedType) paramType).getActualTypeArguments()[0]; + toProperties(props, propNamePfx + "." + fieldName, retList, (Class) elementType); + } catch (Exception e) { + LOG.error("Caught exception trying to convert List returned by " + fromClass.getName() + "." + m.getName() + "() to Properties entry", e); } - } else { - // Class is not yang generated and not a list - // It must be an element of a leaf list - set "prefix" to value - String fromVal = null; - if (fromObj instanceof byte[]) { - try { - fromVal = new String((byte[]) fromObj, "UTF-8"); - LOG.trace("Converted byte array "+pfx+"to string "+ fromVal ); - } catch (Exception e) { - LOG.warn("Caught exception trying to convert "+pfx+" from byte[] to String", e); - fromVal = fromObj.toString(); + } else { + + // Method returns something that is not a List and not + // yang-generated. + // Save its value + try { + String propName = propNamePfx + "." + fieldName; + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + Object propValObj = m.invoke(fromObj); + if (!isAccessible) { + m.setAccessible(isAccessible); + } + + if (propValObj != null) { + if (propValObj instanceof byte[]) { + LOG.trace(m.getName() + " returns a byte[]"); + propVal = new String((byte[]) propValObj, "UTF-8"); + LOG.trace("Converted byte array " + propNamePfx + "." + fieldName + "to string " + propVal); + + } else { + propVal = propValObj.toString(); } + LOG.debug("Setting property " + propName + " to " + propVal); + props.setProperty(propName, propVal); - } else { - fromVal = fromObj.toString(); + } + } catch (Exception e) { + if (m.getName().equals("getKey")) { + LOG.trace("Caught " + e.getClass().getName() + " exception trying to convert results from getKey() - ignoring"); + } else { + LOG.error("Caught exception trying to convert value returned by" + fromClass.getName() + "." + m.getName() + "() to Properties entry", e); + } } - LOG.debug("Setting property " + pfx - + " to " + fromVal); - props.setProperty(pfx, fromVal); + } + + } + } + + // End of method loop. If there was only one getter, named + // "getValue", then + // set value identified by "prefix" to that one value. + if ((numGetters == 1) && ("getValue".equals(lastGetterName))) { + LOG.trace("getValueFIX : " + propNamePfx + " only has getValue() getter - setting " + propNamePfx + " = " + propVal); + props.setProperty(propNamePfx, propVal); + } else { + LOG.trace("getValueFIX : " + propNamePfx + " has " + numGetters + " getter(s), last one found was " + lastGetterName); + + } + + } else { + // Class is not yang generated and not a list + // It must be an element of a leaf list - set "prefix" to value + String fromVal = null; + if (fromObj instanceof byte[]) { + try { + fromVal = new String((byte[]) fromObj, "UTF-8"); + LOG.trace("Converted byte array " + pfx + "to string " + fromVal); + } catch (Exception e) { + LOG.warn("Caught exception trying to convert " + pfx + " from byte[] to String", e); + fromVal = fromObj.toString(); } - return (props); + } else { + fromVal = fromObj.toString(); + } + LOG.debug("Setting property " + pfx + " to " + fromVal); + props.setProperty(pfx, fromVal); } - public static Object toBuilder(Properties props, Object toObj) { + return (props); + } - return (toBuilder(props, "", toObj)); - } + public static Object toBuilder(Properties props, Object toObj) { - public static List toList(Properties props, String pfx, List toObj, - Class elemType) { + return (toBuilder(props, "", toObj)); + } - int maxIdx = -1; - boolean foundValue = false; + public static List toList(Properties props, String pfx, List toObj, Class elemType) { - LOG.trace("Saving properties to List<" + elemType.getName() - + "> from " + pfx); + int maxIdx = -1; + boolean foundValue = false; - if (props.contains(pfx+"_length")) { - try { - int listLength = Integer.parseInt(props.getProperty(pfx+"_length")); - - if (listLength > 0) { - maxIdx = listLength - 1; - } - } catch (Exception e) { - // Ignore exception + LOG.trace("Saving properties to List<" + elemType.getName() + "> from " + pfx); + + if (props.contains(pfx + "_length")) { + try { + int listLength = Integer.parseInt(props.getProperty(pfx + "_length")); + + if (listLength > 0) { + maxIdx = listLength - 1; + } + } catch (Exception e) { + // Ignore exception + } + } + + if (maxIdx == -1) { + // Figure out array size + for (Object pNameObj : props.keySet()) { + String key = (String) pNameObj; + + if (key.startsWith(pfx + "[")) { + String idxStr = key.substring(pfx.length() + 1); + int endloc = idxStr.indexOf("]"); + if (endloc != -1) { + idxStr = idxStr.substring(0, endloc); + } + + try { + int curIdx = Integer.parseInt(idxStr); + if (curIdx > maxIdx) { + maxIdx = curIdx; } + } catch (Exception e) { + LOG.error("Illegal subscript in property " + key); + } + } - - if (maxIdx == -1) { - // Figure out array size - for (Object pNameObj : props.keySet()) { - String key = (String) pNameObj; - - if (key.startsWith(pfx + "[")) { - String idxStr = key.substring(pfx.length() + 1); - int endloc = idxStr.indexOf("]"); - if (endloc != -1) { - idxStr = idxStr.substring(0, endloc); - } + } + } - try { - int curIdx = Integer.parseInt(idxStr); - if (curIdx > maxIdx) { - maxIdx = curIdx; - } - } catch (Exception e) { - LOG.error("Illegal subscript in property " + key); - } + LOG.trace(pfx + " has max index of " + maxIdx); + for (int i = 0; i <= maxIdx; i++) { + + String curBase = pfx + "[" + i + "]"; + + if (isYangGenerated(elemType)) { + String builderName = elemType.getName() + "Builder"; + try { + Class builderClass = Class.forName(builderName); + Object builderObj = builderClass.newInstance(); + Method buildMethod = builderClass.getMethod("build"); + builderObj = toBuilder(props, curBase, builderObj, true); + if (builderObj != null) { + LOG.trace("Calling " + builderObj.getClass().getName() + "." + buildMethod.getName() + "()"); + Object builtObj = buildMethod.invoke(builderObj); + toObj.add(builtObj); + foundValue = true; + } + + } catch (ClassNotFoundException e) { + LOG.warn("Could not find builder class " + builderName, e); + } catch (Exception e) { + LOG.error("Caught exception trying to populate list from " + pfx); + } + } else { + // Must be a leaf list + String curValue = props.getProperty(curBase, ""); - } - } + toObj.add(curValue); + + if ((curValue != null) && (curValue.length() > 0)) { + foundValue = true; } - + } - LOG.trace(pfx + " has max index of " + maxIdx); - for (int i = 0; i <= maxIdx; i++) { + } - String curBase = pfx + "[" + i + "]"; + if (foundValue) { + return (toObj); + } else { + return (null); + } - if (isYangGenerated(elemType)) { - String builderName = elemType.getName() + "Builder"; - try { - Class builderClass = Class.forName(builderName); - Object builderObj = builderClass.newInstance(); - Method buildMethod = builderClass.getMethod("build"); - builderObj = toBuilder(props, curBase, builderObj, true); - if (builderObj != null) { - LOG.trace("Calling " + builderObj.getClass().getName() - + "." + buildMethod.getName() + "()"); - Object builtObj = buildMethod.invoke(builderObj); - toObj.add(builtObj); - foundValue = true; - } + } - } catch (ClassNotFoundException e) { - LOG.warn("Could not find builder class " + builderName, e); - } catch (Exception e) { - LOG.error("Caught exception trying to populate list from " - + pfx); - } - } else { - // Must be a leaf list - String curValue = props.getProperty(curBase, ""); - - toObj.add(curValue); - - if ((curValue != null) && (curValue.length() > 0)) { - foundValue = true; - } - } + public static Object toBuilder(Properties props, String pfx, Object toObj) { + return (toBuilder(props, pfx, toObj, false)); + } - } + public static Object toBuilder(Properties props, String pfx, Object toObj, boolean preservePfx) { + Class toClass = toObj.getClass(); + boolean foundValue = false; + + LOG.trace("Saving properties to " + toClass.getName() + " class from " + pfx); + + Ipv4Address addr; + + if (isYangGenerated(toClass)) { + // Class is yang generated. + LOG.trace(toClass.getName() + " is a Yang-generated class"); - if (foundValue) { - return (toObj); + String propNamePfx = null; + if (preservePfx) { + propNamePfx = pfx; + } else { + + if ((pfx != null) && (pfx.length() > 0)) { + propNamePfx = pfx + "." + toLowerHyphen(toClass.getSimpleName()); } else { - return (null); + propNamePfx = toLowerHyphen(toClass.getSimpleName()); } - } - - public static Object toBuilder(Properties props, String pfx, Object toObj) { - return(toBuilder(props, pfx, toObj, false)); - } + if (propNamePfx.endsWith("-builder")) { + propNamePfx = propNamePfx.substring(0, propNamePfx.length() - "-builder".length()); + } - public static Object toBuilder(Properties props, String pfx, Object toObj, boolean preservePfx) { - Class toClass = toObj.getClass(); - boolean foundValue = false; + if (propNamePfx.endsWith("-impl")) { + propNamePfx = propNamePfx.substring(0, propNamePfx.length() - "-impl".length()); + } + } - LOG.trace("Saving properties to " + toClass.getName() + " class from " - + pfx); + if (toObj instanceof Identifier) { + LOG.trace(toClass.getName() + " is a Key - skipping"); + return (toObj); + } - Ipv4Address addr; + // Iterate through getter methods to figure out values we need to + // set - if (isYangGenerated(toClass)) { - // Class is yang generated. - LOG.trace(toClass.getName() + " is a Yang-generated class"); + for (Method m : toClass.getMethods()) { + if (isSetter(m)) { + Class paramTypes[] = m.getParameterTypes(); + Class paramClass = paramTypes[0]; - String propNamePfx = null; - if (preservePfx) { - propNamePfx = pfx; - } else { + String fieldName = toLowerHyphen(m.getName().substring(3)); + fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); - if ((pfx != null) && (pfx.length() > 0)) { - propNamePfx = pfx + "." - + toLowerHyphen(toClass.getSimpleName()); - } else { - propNamePfx = toLowerHyphen(toClass.getSimpleName()); - } + String propName = propNamePfx + "." + fieldName; - if (propNamePfx.endsWith("-builder")) { - propNamePfx = propNamePfx.substring(0, propNamePfx.length() - - "-builder".length()); - } + String paramValue = props.getProperty(propName); + if (paramValue == null) { + LOG.trace(propName + " is unset"); + } else { + LOG.trace(propName + " = " + paramValue); + } - if (propNamePfx.endsWith("-impl")) { - propNamePfx = propNamePfx.substring(0, propNamePfx.length() - - "-impl".length()); + // Is the return type a yang generated class? + if (isYangGenerated(paramClass)) { + // Is it an enum? + if (paramClass.isEnum()) { + + LOG.trace(m.getName() + " expects an Enum"); + // Param type is a typedef. + if ((paramValue != null) && (paramValue.length() > 0)) { + Object paramObj = null; + + try { + paramObj = Enum.valueOf(paramClass, toJavaEnum(paramValue)); + } catch (Exception e) { + LOG.error("Caught exception trying to convert field " + propName + " to enum " + paramClass.getName(), e); } - } - if (toObj instanceof Identifier) { - LOG.trace(toClass.getName() + " is a Key - skipping"); - return (toObj); - } + try { + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + + LOG.trace("Calling " + toObj.getClass().getName() + "." + m.getName() + "(" + paramValue + ")"); + m.invoke(toObj, paramObj); - // Iterate through getter methods to figure out values we need to - // set + if (!isAccessible) { + m.setAccessible(isAccessible); + } + foundValue = true; - for (Method m : toClass.getMethods()) { - if (isSetter(m)) { - Class paramTypes[] = m.getParameterTypes(); - Class paramClass = paramTypes[0]; + } catch (Exception e) { + LOG.error("Caught exception trying to create Yang-generated enum expected by" + toClass.getName() + "." + m.getName() + "() from Properties entry", e); + } + } + } else { + + String simpleName = paramClass.getSimpleName(); + + if ("Ipv4Address".equals(simpleName) || "Ipv6Address".equals(simpleName) || "IpAddress".equals(simpleName)) { - String fieldName = toLowerHyphen(m.getName().substring(3)); - fieldName = fieldName.substring(0, 1).toLowerCase() - + fieldName.substring(1); + if ((paramValue != null) && (paramValue.length() > 0)) { + try { + IpAddress ipAddr = IpAddressBuilder.getDefaultInstance(paramValue); - String propName = propNamePfx + "." + fieldName; + if ("Ipv4Address".equals(simpleName)) { + m.invoke(toObj, ipAddr.getIpv4Address()); + } else if ("Ipv6Address".equals(simpleName)) { + m.invoke(toObj, ipAddr.getIpv6Address()); - String paramValue = props.getProperty(propName); - if (paramValue == null) { - LOG.trace(propName + " is unset"); } else { - LOG.trace(propName + " = " + paramValue); + m.invoke(toObj, ipAddr); } + foundValue = true; + } catch (Exception e) { + LOG.error("Caught exception calling " + toClass.getName() + "." + m.getName() + "(" + paramValue + ")", e); - // Is the return type a yang generated class? - if (isYangGenerated(paramClass)) { - // Is it an enum? - if (paramClass.isEnum()) { - - LOG.trace(m.getName() + " expects an Enum"); - // Param type is a typedef. - if ((paramValue != null) && (paramValue.length() > 0)) { - Object paramObj = null; - - try { - paramObj = Enum.valueOf(paramClass, - toUpperCamelCase(paramValue)); - } catch (Exception e) { - LOG.error( - "Caught exception trying to convert field " - + propName + " to enum " - + paramClass.getName(), e); - } - - try { - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - - LOG.trace("Calling " - + toObj.getClass().getName() + "." - + m.getName() + "(" + paramValue - + ")"); - m.invoke(toObj, paramObj); - - if (!isAccessible) { - m.setAccessible(isAccessible); - } - foundValue = true; - - } catch (Exception e) { - LOG.error( - "Caught exception trying to create Yang-generated enum expected by" - + toClass.getName() - + "." - + m.getName() - + "() from Properties entry", - e); - } - } - } else { - - String simpleName = paramClass.getSimpleName(); - - if ("Ipv4Address".equals(simpleName) - || "Ipv6Address".equals(simpleName) || "IpAddress".equals(simpleName)) { - - if ((paramValue != null) && (paramValue.length() > 0)) { - try { - IpAddress ipAddr = IpAddressBuilder - .getDefaultInstance(paramValue); - - - if ("Ipv4Address".equals(simpleName)) - { - m.invoke(toObj, ipAddr.getIpv4Address()); - } - else if ("Ipv6Address".equals(simpleName)) - { - m.invoke(toObj, ipAddr.getIpv6Address()); - - } - else - { - m.invoke(toObj, ipAddr); - } - foundValue = true; - } catch (Exception e) { - LOG.error( - "Caught exception calling " - + toClass.getName() + "." - + m.getName() + "(" - + paramValue + ")", e); - - } - } else { - try { - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - LOG.trace("Calling " - + toObj.getClass().getName() - + "." + m.getName() + "(" - + paramValue + ")"); - m.invoke(toObj, paramValue); - if (!isAccessible) { - m.setAccessible(isAccessible); - } - foundValue = true; - - } catch (Exception e) { - LOG.error( - "Caught exception trying to call " - + toClass.getName() - + "." - + m.getName() - + "() with Properties entry", - e); - } - } - } else if ("IpPrefix".equals(simpleName)) { - if ((paramValue != null) && (paramValue.length() > 0)) { - try { - IpPrefix ipPrefix = IpPrefixBuilder.getDefaultInstance(paramValue); - m.invoke(toObj, ipPrefix); - foundValue = true; - } catch (Exception e) { - LOG.error( - "Caught exception calling " - + toClass.getName() + "." - + m.getName() + "(" - + paramValue + ")", e); - } - } - } else { - // setter expects a yang-generated class. Need - // to - // create a builder to set it. - - String builderName = paramClass.getName() - + "Builder"; - Class builderClass = null; - Object builderObj = null; - Object paramObj = null; - - Object constObj = null; - - LOG.trace(m.getName() - + " expects a yang-generated class - looking for builder " - + builderName); - try { - builderClass = Class.forName(builderName); - builderObj = builderClass.newInstance(); - paramObj = toBuilder(props, propNamePfx, - builderObj); - } catch (ClassNotFoundException e) { - - if (paramValue == null) { - try { - boolean isAccessible = m - .isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - LOG.trace("Calling " - + toObj.getClass() - .getName() + "." - + m.getName() + "(null)"); - m.invoke(toObj, new Object[]{null}); - if (!isAccessible) { - m.setAccessible(isAccessible); - } - foundValue = true; - - } catch (Exception e1) { - LOG.error( - "Caught exception trying to cally" - + toClass.getName() - + "." - + m.getName() - + "() with Properties entry", - e1); - } - } else { - try { - // See if I can find a constructor I - // can - // use - Constructor[] constructors = paramClass - .getConstructors(); - // Is there a String constructor? - for (Constructor c : constructors) { - Class[] cParms = c - .getParameterTypes(); - if ((cParms != null) - && (cParms.length == 1)) { - if (String.class - .isAssignableFrom(cParms[0])) { - constObj = c - .newInstance(paramValue); - } - } - } - - if (constObj == null) { - // Is there a Long constructor? - for (Constructor c : constructors) { - Class[] cParms = c - .getParameterTypes(); - if ((cParms != null) - && (cParms.length == 1)) { - if (Long.class - .isAssignableFrom(cParms[0])) { - constObj = c - .newInstance(Long - .parseLong(paramValue)); - } - } - } - - } - - if (constObj == null) { - - // Last chance - see if - // parameter class has a static - // method - // getDefaultInstance(String) - try { - Method gm = paramClass - .getMethod( - "getDefaultInstance", - String.class); - - int gmodifier = gm - .getModifiers(); - if (Modifier - .isStatic(gmodifier)) { - // Invoke static - // getDefaultInstance(String) - paramObj = gm.invoke( - null, - paramValue); - } - - } catch (Exception gme) { - // Ignore exceptions - } - } - - - } catch (Exception e1) { - LOG.warn( - "Could not find a suitable constructor for " - + paramClass - .getName(), - e1); - } - - if (constObj == null) { - LOG.warn("Could not find builder class " - + builderName - + " and could not find a String or Long constructor or static getDefaultInstance(String) - trying just to set passing paramValue"); - - } - } - } catch (Exception e) { - LOG.error( - "Caught exception trying to create builder " - + builderName, e); - } - - if (paramObj != null) { - - try { - - Method buildMethod = builderClass - .getMethod("build"); - LOG.trace("Calling " - + paramObj.getClass().getName() - + "." + buildMethod.getName() - + "()"); - Object builtObj = buildMethod - .invoke(paramObj); - - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - - LOG.trace("Calling " - + toObj.getClass().getName() - + "." + m.getName() + "()"); - m.invoke(toObj, builtObj); - if (!isAccessible) { - m.setAccessible(isAccessible); - } - foundValue = true; - - } catch (Exception e) { - LOG.error( - "Caught exception trying to set Yang-generated class expected by" - + toClass.getName() - + "." - + m.getName() - + "() from Properties entry", - e); - } - } else { - try { - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - - if (constObj != null) { - - LOG.trace("Calling " - + toObj.getClass() - .getName() + "." - + m.getName() + "(" - + constObj.toString() + ")"); - m.invoke(toObj, constObj); - } else { - LOG.trace("Calling " - + toObj.getClass() - .getName() + "." - + m.getName() + "(" - + paramValue + ")"); - m.invoke(toObj, paramValue); - - } - if (!isAccessible) { - m.setAccessible(isAccessible); - } - foundValue = true; - - } catch (Exception e) { - LOG.error( - "Caught exception trying to convert value returned by" - + toClass.getName() - + "." - + m.getName() - + "() to Properties entry", - e); - } - } - } - } - } else { + } + } else { + try { + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + LOG.trace("Calling " + toObj.getClass().getName() + "." + m.getName() + "(" + paramValue + ")"); + m.invoke(toObj, paramValue); + if (!isAccessible) { + m.setAccessible(isAccessible); + } + foundValue = true; - // Setter's argument is not a yang-generated class. See - // if it is a List. + } catch (Exception e) { + LOG.error("Caught exception trying to call " + toClass.getName() + "." + m.getName() + "() with Properties entry", e); + } + } + } else if ("IpPrefix".equals(simpleName)) { + if ((paramValue != null) && (paramValue.length() > 0)) { + try { + IpPrefix ipPrefix = IpPrefixBuilder.getDefaultInstance(paramValue); + m.invoke(toObj, ipPrefix); + foundValue = true; + } catch (Exception e) { + LOG.error("Caught exception calling " + toClass.getName() + "." + m.getName() + "(" + paramValue + ")", e); + } + } + } else { + // setter expects a yang-generated class. Need + // to + // create a builder to set it. - if (List.class.isAssignableFrom(paramClass)) { + String builderName = paramClass.getName() + "Builder"; + Class builderClass = null; + Object builderObj = null; + Object paramObj = null; - LOG.trace("Parameter class " + paramClass.getName() - + " is a List"); + Object constObj = null; - // Figure out what type of args are in List and pass - // that to toList(). + LOG.trace(m.getName() + " expects a yang-generated class - looking for builder " + builderName); + try { + builderClass = Class.forName(builderName); + builderObj = builderClass.newInstance(); + paramObj = toBuilder(props, propNamePfx, builderObj); + } catch (ClassNotFoundException e) { - Type paramType = m.getGenericParameterTypes()[0]; - Type elementType = ((ParameterizedType) paramType) - .getActualTypeArguments()[0]; - Object paramObj = new LinkedList(); - try { - paramObj = toList(props, propName, - (List) paramObj, (Class) elementType); - } catch (Exception e) { - LOG.error("Caught exception trying to create list expected as argument to " - + toClass.getName() + "." + m.getName()); + if (paramValue == null) { + try { + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + LOG.trace("Calling " + toObj.getClass().getName() + "." + m.getName() + "(null)"); + m.invoke(toObj, new Object[] { null }); + if (!isAccessible) { + m.setAccessible(isAccessible); + } + foundValue = true; + + } catch (Exception e1) { + LOG.error("Caught exception trying to cally" + toClass.getName() + "." + m.getName() + "() with Properties entry", e1); + } + } else { + try { + // See if I can find a constructor I + // can + // use + Constructor[] constructors = paramClass.getConstructors(); + // Is there a String constructor? + for (Constructor c : constructors) { + Class[] cParms = c.getParameterTypes(); + if ((cParms != null) && (cParms.length == 1)) { + if (String.class.isAssignableFrom(cParms[0])) { + constObj = c.newInstance(paramValue); + } + } + } + + if (constObj == null) { + // Is there a Long constructor? + for (Constructor c : constructors) { + Class[] cParms = c.getParameterTypes(); + if ((cParms != null) && (cParms.length == 1)) { + if (Long.class.isAssignableFrom(cParms[0])) { + constObj = c.newInstance(Long.parseLong(paramValue)); } + } + } - if (paramObj != null) { - try { - boolean isAccessible = m.isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - LOG.trace("Calling " - + toObj.getClass().getName() + "." - + m.getName() + "(" + paramValue - + ")"); - m.invoke(toObj, paramObj); - if (!isAccessible) { - m.setAccessible(isAccessible); - } - foundValue = true; - - } catch (Exception e) { - LOG.error( - "Caught exception trying to convert List returned by" - + toClass.getName() + "." - + m.getName() - + "() to Properties entry", - e); - } - } - } else { - - // Setter expects something that is not a List and - // not yang-generated. Just pass the parameter value - - LOG.trace("Parameter class " - + paramClass.getName() - + " is not a yang-generated class or a List"); - - if ((paramValue != null) && (paramValue.length() > 0)) { - - Object constObj = null; - - try { - // See if I can find a constructor I can use - Constructor[] constructors = paramClass - .getConstructors(); - // Is there a String constructor? - for (Constructor c : constructors) { - Class[] cParms = c.getParameterTypes(); - if ((cParms != null) - && (cParms.length == 1)) { - if (String.class - .isAssignableFrom(cParms[0])) { - constObj = c - .newInstance(paramValue); - } - } - } - - if (constObj == null) { - // Is there a Long constructor? - for (Constructor c : constructors) { - Class[] cParms = c - .getParameterTypes(); - if ((cParms != null) - && (cParms.length == 1)) { - if (Long.class - .isAssignableFrom(cParms[0])) { - constObj = c - .newInstance(Long - .parseLong(paramValue)); - } - } - } - - } - - if (constObj != null) { - try { - LOG.trace("Calling " - + toObj.getClass() - .getName() + "." - + m.getName() + "(" - + constObj + ")"); - m.invoke(toObj, constObj); - foundValue = true; - } catch (Exception e2) { - LOG.error( - "Caught exception trying to call " - + m.getName(), e2); - } - } else { - try { - boolean isAccessible = m - .isAccessible(); - if (!isAccessible) { - m.setAccessible(true); - } - LOG.trace("Calling " - + toObj.getClass() - .getName() + "." - + m.getName() + "(" - + paramValue + ")"); - m.invoke(toObj, paramValue); - if (!isAccessible) { - m.setAccessible(isAccessible); - } - foundValue = true; - - } catch (Exception e) { - LOG.error( - "Caught exception trying to convert value returned by" - + toClass.getName() - + "." - + m.getName() - + "() to Properties entry", - e); - } - } - } catch (Exception e1) { - LOG.warn( - "Could not find a suitable constructor for " - + paramClass.getName(), e1); - } + } + if (constObj == null) { - } + // Last chance - see if + // parameter class has a static + // method + // getDefaultInstance(String) + try { + Method gm = paramClass.getMethod("getDefaultInstance", String.class); + + int gmodifier = gm.getModifiers(); + if (Modifier.isStatic(gmodifier)) { + // Invoke static + // getDefaultInstance(String) + paramObj = gm.invoke(null, paramValue); + } + + } catch (Exception gme) { + // Ignore exceptions } + } + + } catch (Exception e1) { + LOG.warn("Could not find a suitable constructor for " + paramClass.getName(), e1); } - } // End of section handling "setter" method - } // End of loop through Methods - } // End of section handling yang-generated class - if (foundValue) { - return (toObj); - } else { - return (null); - } - } + if (constObj == null) { + LOG.warn("Could not find builder class " + builderName + " and could not find a String or Long constructor or static getDefaultInstance(String) - trying just to set passing paramValue"); - public static void printPropertyList(PrintStream pstr, String pfx, - Class toClass) { - boolean foundValue = false; + } + } + } catch (Exception e) { + LOG.error("Caught exception trying to create builder " + builderName, e); + } - LOG.trace("Analyzing " + toClass.getName() + " class : pfx " + pfx); + if (paramObj != null) { - if (isYangGenerated(toClass) - && (!Identifier.class.isAssignableFrom(toClass))) { - // Class is yang generated. - LOG.trace(toClass.getName() + " is a Yang-generated class"); + try { - if (toClass.getName().endsWith("Key")) { - if (Identifier.class.isAssignableFrom(toClass)) { - LOG.trace(Identifier.class.getName() - + " is assignable from " + toClass.getName()); - } else { + Method buildMethod = builderClass.getMethod("build"); + LOG.trace("Calling " + paramObj.getClass().getName() + "." + buildMethod.getName() + "()"); + Object builtObj = buildMethod.invoke(paramObj); - LOG.trace(Identifier.class.getName() - + " is NOT assignable from " + toClass.getName()); - } - } + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } - String propNamePfx = null; - if (pfx.endsWith("]")) { - propNamePfx = pfx; - } else { + LOG.trace("Calling " + toObj.getClass().getName() + "." + m.getName() + "()"); + m.invoke(toObj, builtObj); + if (!isAccessible) { + m.setAccessible(isAccessible); + } + foundValue = true; - if ((pfx != null) && (pfx.length() > 0)) { - propNamePfx = pfx + "." - + toLowerHyphen(toClass.getSimpleName()); + } catch (Exception e) { + LOG.error("Caught exception trying to set Yang-generated class expected by" + toClass.getName() + "." + m.getName() + "() from Properties entry", e); + } } else { - propNamePfx = toLowerHyphen(toClass.getSimpleName()); - } + try { + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } - if (propNamePfx.endsWith("-builder")) { - propNamePfx = propNamePfx.substring(0, propNamePfx.length() - - "-builder".length()); - } + if (constObj != null) { - if (propNamePfx.endsWith("-impl")) { - propNamePfx = propNamePfx.substring(0, propNamePfx.length() - - "-impl".length()); + LOG.trace("Calling " + toObj.getClass().getName() + "." + m.getName() + "(" + constObj.toString() + ")"); + m.invoke(toObj, constObj); + } else { + LOG.trace("Calling " + toObj.getClass().getName() + "." + m.getName() + "(" + paramValue + ")"); + m.invoke(toObj, paramValue); + + } + if (!isAccessible) { + m.setAccessible(isAccessible); + } + foundValue = true; + + } catch (Exception e) { + LOG.error("Caught exception trying to convert value returned by" + toClass.getName() + "." + m.getName() + "() to Properties entry", e); + } } + } } + } else { - // Iterate through getter methods to figure out values we need to - // set - - for (Method m : toClass.getMethods()) { - LOG.trace("Is " + m.getName() + " method a getter?"); - if (isGetter(m)) { - LOG.trace(m.getName() + " is a getter"); - Class returnClass = m.getReturnType(); - - String fieldName = toLowerHyphen(m.getName().substring(3)); - fieldName = fieldName.substring(0, 1).toLowerCase() - + fieldName.substring(1); - - String propName = propNamePfx + "." + fieldName; - - // Is the return type a yang generated class? - if (isYangGenerated(returnClass)) { - // Is it an enum? - if (returnClass.isEnum()) { - - LOG.trace(m.getName() + " is an Enum"); - pstr.print("\n\n * " + propName); - - } else { - - String simpleName = returnClass.getSimpleName(); - - if ("Ipv4Address".equals(simpleName) || "Ipv6Address".equals(simpleName) || "IpAddress".equals(simpleName) || "IpPrefix".equals(simpleName)) { - LOG.trace(m.getName()+" is an "+simpleName); - pstr.print("\n\n * " + propName); - } else { - printPropertyList(pstr, propNamePfx, returnClass); - } + // Setter's argument is not a yang-generated class. See + // if it is a List. - } - } else { + if (List.class.isAssignableFrom(paramClass)) { - // Setter's argument is not a yang-generated class. See - // if it is a List. + LOG.trace("Parameter class " + paramClass.getName() + " is a List"); - if (List.class.isAssignableFrom(returnClass)) { + // Figure out what type of args are in List and pass + // that to toList(). - LOG.trace("Parameter class " - + returnClass.getName() + " is a List"); + Type paramType = m.getGenericParameterTypes()[0]; + Type elementType = ((ParameterizedType) paramType).getActualTypeArguments()[0]; + Object paramObj = new LinkedList(); + try { + paramObj = toList(props, propName, (List) paramObj, (Class) elementType); + } catch (Exception e) { + LOG.error("Caught exception trying to create list expected as argument to " + toClass.getName() + "." + m.getName()); + } - // Figure out what type of args are in List and pass - // that to toList(). + if (paramObj != null) { + try { + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + LOG.trace("Calling " + toObj.getClass().getName() + "." + m.getName() + "(" + paramValue + ")"); + m.invoke(toObj, paramObj); + if (!isAccessible) { + m.setAccessible(isAccessible); + } + foundValue = true; - Type returnType = m.getGenericReturnType(); - Type elementType = ((ParameterizedType) returnType) - .getActualTypeArguments()[0]; - Class elementClass = (Class) elementType; - LOG.trace("Calling printPropertyList on list type (" - + elementClass.getName() - + "), pfx is (" - + pfx - + "), toClass is (" - + toClass.getName() + ")"); - printPropertyList( - pstr, - propNamePfx - + "." - + toLowerHyphen(elementClass - .getSimpleName()) + "[]", - elementClass); + } catch (Exception e) { + LOG.error("Caught exception trying to convert List returned by" + toClass.getName() + "." + m.getName() + "() to Properties entry", e); + } + } + } else { - } else if (!returnClass.equals(Class.class)) { + // Setter expects something that is not a List and + // not yang-generated. Just pass the parameter value - // Setter expects something that is not a List and - // not yang-generated. Just pass the parameter value + LOG.trace("Parameter class " + paramClass.getName() + " is not a yang-generated class or a List"); - LOG.trace("Parameter class " - + returnClass.getName() - + " is not a yang-generated class or a List"); + if ((paramValue != null) && (paramValue.length() > 0)) { - pstr.print("\n\n * " + propName); + Object constObj = null; + try { + // See if I can find a constructor I can use + Constructor[] constructors = paramClass.getConstructors(); + // Is there a String constructor? + for (Constructor c : constructors) { + Class[] cParms = c.getParameterTypes(); + if ((cParms != null) && (cParms.length == 1)) { + if (String.class.isAssignableFrom(cParms[0])) { + constObj = c.newInstance(paramValue); + } + } + } + + if (constObj == null) { + // Is there a Long constructor? + for (Constructor c : constructors) { + Class[] cParms = c.getParameterTypes(); + if ((cParms != null) && (cParms.length == 1)) { + if (Long.class.isAssignableFrom(cParms[0])) { + constObj = c.newInstance(Long.parseLong(paramValue)); } + } } - } // End of section handling "setter" method - } // End of loop through Methods - } // End of section handling yang-generated class + } + + if (constObj != null) { + try { + LOG.trace("Calling " + toObj.getClass().getName() + "." + m.getName() + "(" + constObj + ")"); + m.invoke(toObj, constObj); + foundValue = true; + } catch (Exception e2) { + LOG.error("Caught exception trying to call " + m.getName(), e2); + } + } else { + try { + boolean isAccessible = m.isAccessible(); + if (!isAccessible) { + m.setAccessible(true); + } + LOG.trace("Calling " + toObj.getClass().getName() + "." + m.getName() + "(" + paramValue + ")"); + m.invoke(toObj, paramValue); + if (!isAccessible) { + m.setAccessible(isAccessible); + } + foundValue = true; + + } catch (Exception e) { + LOG.error("Caught exception trying to convert value returned by" + toClass.getName() + "." + m.getName() + "() to Properties entry", e); + } + } + } catch (Exception e1) { + LOG.warn("Could not find a suitable constructor for " + paramClass.getName(), e1); + } + + } + } + } + } // End of section handling "setter" method + } // End of loop through Methods + } // End of section handling yang-generated class + + if (foundValue) { + return (toObj); + } else { + return (null); } + } + + public static void printPropertyList(PrintStream pstr, String pfx, Class toClass) { + boolean foundValue = false; + + LOG.trace("Analyzing " + toClass.getName() + " class : pfx " + pfx); - public static boolean isYangGenerated(Class c) { - if (c == null) { - return (false); + if (isYangGenerated(toClass) && (!Identifier.class.isAssignableFrom(toClass))) { + // Class is yang generated. + LOG.trace(toClass.getName() + " is a Yang-generated class"); + + if (toClass.getName().endsWith("Key")) { + if (Identifier.class.isAssignableFrom(toClass)) { + LOG.trace(Identifier.class.getName() + " is assignable from " + toClass.getName()); } else { - return (c.getName().startsWith("org.opendaylight.yang.gen.")); + + LOG.trace(Identifier.class.getName() + " is NOT assignable from " + toClass.getName()); } - } - - public static boolean isIpPrefix(Class c) { - - if (c == null ) { - return (false); + } + + String propNamePfx = null; + if (pfx.endsWith("]")) { + propNamePfx = pfx; + } else { + + if ((pfx != null) && (pfx.length() > 0)) { + propNamePfx = pfx + "." + toLowerHyphen(toClass.getSimpleName()); + } else { + propNamePfx = toLowerHyphen(toClass.getSimpleName()); } - String simpleName = c.getSimpleName(); - return ("IpPrefix".equals(simpleName)) ; - } - - - - public static boolean isIpv4Address(Class c) { - - if (c == null ) { - return (false); + + if (propNamePfx.endsWith("-builder")) { + propNamePfx = propNamePfx.substring(0, propNamePfx.length() - "-builder".length()); } - String simpleName = c.getSimpleName(); - return ("Ipv4Address".equals(simpleName)) ; + + if (propNamePfx.endsWith("-impl")) { + propNamePfx = propNamePfx.substring(0, propNamePfx.length() - "-impl".length()); + } + } + + // Iterate through getter methods to figure out values we need to + // set + + for (Method m : toClass.getMethods()) { + LOG.trace("Is " + m.getName() + " method a getter?"); + if (isGetter(m)) { + LOG.trace(m.getName() + " is a getter"); + Class returnClass = m.getReturnType(); + + String fieldName = toLowerHyphen(m.getName().substring(3)); + fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); + + String propName = propNamePfx + "." + fieldName; + + // Is the return type a yang generated class? + if (isYangGenerated(returnClass)) { + // Is it an enum? + if (returnClass.isEnum()) { + + LOG.trace(m.getName() + " is an Enum"); + pstr.print("\n\n * " + propName); + + } else { + + String simpleName = returnClass.getSimpleName(); + + if ("Ipv4Address".equals(simpleName) || "Ipv6Address".equals(simpleName) || "IpAddress".equals(simpleName) || "IpPrefix".equals(simpleName)) { + LOG.trace(m.getName() + " is an " + simpleName); + pstr.print("\n\n * " + propName); + } else { + printPropertyList(pstr, propNamePfx, returnClass); + } + + } + } else { + + // Setter's argument is not a yang-generated class. See + // if it is a List. + + if (List.class.isAssignableFrom(returnClass)) { + + LOG.trace("Parameter class " + returnClass.getName() + " is a List"); + + // Figure out what type of args are in List and pass + // that to toList(). + + Type returnType = m.getGenericReturnType(); + Type elementType = ((ParameterizedType) returnType).getActualTypeArguments()[0]; + Class elementClass = (Class) elementType; + LOG.trace("Calling printPropertyList on list type (" + elementClass.getName() + "), pfx is (" + pfx + "), toClass is (" + toClass.getName() + ")"); + printPropertyList(pstr, propNamePfx + "." + toLowerHyphen(elementClass.getSimpleName()) + "[]", elementClass); + + } else if (!returnClass.equals(Class.class)) { + + // Setter expects something that is not a List and + // not yang-generated. Just pass the parameter value + + LOG.trace("Parameter class " + returnClass.getName() + " is not a yang-generated class or a List"); + + pstr.print("\n\n * " + propName); + + } + } + } // End of section handling "setter" method + } // End of loop through Methods + } // End of section handling yang-generated class + + } + + public static boolean isYangGenerated(Class c) { + if (c == null) { + return (false); + } else { + return (c.getName().startsWith("org.opendaylight.yang.gen.")); } - - public static boolean isIpv6Address(Class c) { - - if (c == null ) { - return (false); - } - String simpleName = c.getSimpleName(); - return ("Ipv6Address".equals(simpleName)) ; + } + + public static boolean isIpPrefix(Class c) { + + if (c == null) { + return (false); } - - public static boolean isIpAddress(Class c) { - - if (c == null ) { - return (false); - } - String simpleName = c.getSimpleName(); - return ("IpAddress".equals(simpleName)) ; + String simpleName = c.getSimpleName(); + return ("IpPrefix".equals(simpleName)); + } + + public static boolean isIpv4Address(Class c) { + + if (c == null) { + return (false); } + String simpleName = c.getSimpleName(); + return ("Ipv4Address".equals(simpleName)); + } - public static String toLowerHyphen(String inStr) { - if (inStr == null) { - return (null); - } + public static boolean isIpv6Address(Class c) { - String str = inStr.substring(0, 1).toLowerCase(); - if (inStr.length() > 1) { - str = str + inStr.substring(1); - } + if (c == null) { + return (false); + } + String simpleName = c.getSimpleName(); + return ("Ipv6Address".equals(simpleName)); + } + + public static boolean isIpAddress(Class c) { - String regex = "(([a-z0-9])([A-Z]))"; - String replacement = "$2-$3"; + if (c == null) { + return (false); + } + String simpleName = c.getSimpleName(); + return ("IpAddress".equals(simpleName)); + } - String retval = str.replaceAll(regex, replacement).toLowerCase(); + public static String toLowerHyphen(String inStr) { + if (inStr == null) { + return (null); + } - LOG.trace("Converting " + inStr + " => " + str + " => " + retval); - return (retval); + String str = inStr.substring(0, 1).toLowerCase(); + if (inStr.length() > 1) { + str = str + inStr.substring(1); } - public static String toUpperCamelCase(String inStr) { - if (inStr == null) { - return (null); - } else if (inStr.length() == 0) { - return(inStr); - } + String regex = "(([a-z0-9])([A-Z]))"; + String replacement = "$2-$3"; - String[] terms = inStr.split("-"); - StringBuffer sbuff = new StringBuffer(); - // Check if string begins with a digit - if (Character.isDigit(inStr.charAt(0))) { - sbuff.append('_'); - } - for (String term : terms) { - sbuff.append(term.substring(0, 1).toUpperCase()); - if (term.length() > 1) { - sbuff.append(term.substring(1)); - } - } - return (sbuff.toString()); + String retval = str.replaceAll(regex, replacement).toLowerCase(); + + LOG.trace("Converting " + inStr + " => " + str + " => " + retval); + return (retval); + } + //This is called when mapping the yang value back to a valid java enumeration + public static String toJavaEnum(String inStr) { + if (inStr == null) { + return (null); + } else if (inStr.length() == 0) { + return (inStr); } - public static boolean isGetter(Method m) { - if (m == null) { - return (false); - } + //This will strip out all periods, which cannot be in a java enum + inStr = inStr.replaceAll("\\.", ""); - if (Modifier.isPublic(m.getModifiers()) - && (m.getParameterTypes().length == 0)) { - if (m.getName().matches("^get[A-Z].*") - && !m.getReturnType().equals(void.class)) { - if (!"getClass".equals(m.getName())) { - return (true); - } - } + String[] terms = inStr.split("-"); + StringBuffer sbuff = new StringBuffer(); - if (m.getName().matches("^get[A-Z].*") - && m.getReturnType().equals(boolean.class)) { - return (true); - } - - if (m.getName().matches("^is[A-Z].*") - && m.getReturnType().equals(Boolean.class)) { - return(true); - } - } + //appends an _ if the string starts with a digit to make it a valid java enum + if (Character.isDigit(inStr.charAt(0))) { + sbuff.append('_'); + } + //If the string contains hyphens it will convert the string to upperCamelCase without hyphens + for (String term : terms) { + sbuff.append(term.substring(0, 1).toUpperCase()); + if (term.length() > 1) { + sbuff.append(term.substring(1)); + } + } + return (sbuff.toString()); + + } - return (false); + public static boolean isGetter(Method m) { + if (m == null) { + return (false); } - public static boolean isSetter(Method m) { - if (m == null) { - return (false); + if (Modifier.isPublic(m.getModifiers()) && (m.getParameterTypes().length == 0)) { + if (m.getName().matches("^get[A-Z].*") && !m.getReturnType().equals(void.class)) { + if (!"getClass".equals(m.getName())) { + return (true); } + } - if (Modifier.isPublic(m.getModifiers()) - && (m.getParameterTypes().length == 1)) { - if (m.getName().matches("^set[A-Z].*")) { - Class[] paramTypes = m.getParameterTypes(); - if (paramTypes[0].isAssignableFrom(Identifier.class) - || Identifier.class.isAssignableFrom(paramTypes[0])) { - return (false); - } else { - return (true); - } - } + if (m.getName().matches("^get[A-Z].*") && m.getReturnType().equals(boolean.class)) { + return (true); + } + + if (m.getName().matches("^is[A-Z].*") && m.getReturnType().equals(Boolean.class)) { + return (true); + } + } + + return (false); + } + public static boolean isSetter(Method m) { + if (m == null) { + return (false); + } + + if (Modifier.isPublic(m.getModifiers()) && (m.getParameterTypes().length == 1)) { + if (m.getName().matches("^set[A-Z].*")) { + Class[] paramTypes = m.getParameterTypes(); + if (paramTypes[0].isAssignableFrom(Identifier.class) || Identifier.class.isAssignableFrom(paramTypes[0])) { + return (false); + } else { + return (true); } + } - return (false); } + return (false); + } + + public static String getFullPropertiesPath(String propertiesFileName) { + return "/opt/bvc/controller/configuration/" + propertiesFileName; + } + + //This is called when mapping a valid java enumeration back to the yang model value + public static String mapEnumeratedValue(String propertyName, String propertyValue) { + LOG.info("mapEnumeratedValue called with propertyName=" + propertyName + " and value=" + propertyValue); + String mappingKey = "yang." + propertyName + "." + propertyValue; + if (yangMappingProperties.containsKey(mappingKey)) { + return (yangMappingProperties.getProperty(mappingKey)); + } else { + LOG.info("yangMappingProperties did not contain the key " + mappingKey + " returning the original value."); + return propertyValue; + } + } + } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/NotifyNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/NotifyNodeExecutor.java index 5002604..4dc4a93 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/NotifyNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/NotifyNodeExecutor.java @@ -56,15 +56,7 @@ public class NotifyNodeExecutor extends SvcLogicNodeExecutor { + plugin); } - BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) - .getBundleContext(); - - ServiceReference sref = bctx.getServiceReference(plugin); - - if (sref != null) { - SvcLogicResource resourcePlugin = (SvcLogicResource) bctx - .getService(sref); - + SvcLogicResource resourcePlugin = getSvcLogicResource(plugin); if (resourcePlugin != null) { try { @@ -88,10 +80,6 @@ public class NotifyNodeExecutor extends SvcLogicNodeExecutor { LOG.warn("Could not find SvcLogicResource object for plugin " + plugin); } - } else { - LOG.warn("Could not find service reference object for plugin " - + plugin); - } SvcLogicNode nextNode = node.getOutcomeValue(outValue); if (nextNode != null) { @@ -114,5 +102,21 @@ public class NotifyNodeExecutor extends SvcLogicNodeExecutor { return (nextNode); } + protected SvcLogicResource getSvcLogicResource(String plugin) { + BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) + .getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(plugin); + if (sref != null) { + SvcLogicResource resourcePlugin = (SvcLogicResource) bctx + .getService(sref); + return resourcePlugin; + } + else { + LOG.warn("Could not find service reference object for plugin " + plugin); + return null; + } + } + } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/RecordNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/RecordNodeExecutor.java index d07a4d6..f75752b 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/RecordNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/RecordNodeExecutor.java @@ -50,8 +50,8 @@ public class RecordNodeExecutor extends SvcLogicNodeExecutor { node.getAttribute("plugin"), node, ctx); String outValue = "failure"; - if (LOG.isDebugEnabled()) { - LOG.debug(node.getNodeType() + if (LOG.isTraceEnabled()) { + LOG.trace(node.getNodeType() + " node encountered - looking for recorder class " + plugin); } @@ -71,21 +71,15 @@ public class RecordNodeExecutor extends SvcLogicNodeExecutor { String curExprValue = SvcLogicExpressionResolver.evaluate(curExpr, node, ctx); - if (LOG.isDebugEnabled()) { - LOG.debug("executeRecordNode : parameter " + curName + " = " + if (LOG.isTraceEnabled()) { + LOG.trace("executeRecordNode : parameter " + curName + " = " + curExpr + " => " + curExprValue); } parmMap.put(curName, curExprValue); } - BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) - .getBundleContext(); - - ServiceReference sref = bctx.getServiceReference(plugin); - if (sref != null) { - SvcLogicRecorder recorder = (SvcLogicRecorder) bctx - .getService(sref); + SvcLogicRecorder recorder = getSvcLogicResource(plugin); if (recorder != null) { @@ -99,9 +93,6 @@ public class RecordNodeExecutor extends SvcLogicNodeExecutor { LOG.warn("Could not find SvcLogicRecorder object for plugin " + plugin); } - } else { - LOG.warn("Cound not find service reference for plugin " + plugin); - } SvcLogicNode nextNode = node.getOutcomeValue(outValue); if (nextNode != null) { @@ -117,13 +108,28 @@ public class RecordNodeExecutor extends SvcLogicNodeExecutor { LOG.debug("about to execute Other branch"); } } else { - if (LOG.isDebugEnabled()) { - LOG.debug("no failure or Other branch found"); + if (LOG.isTraceEnabled()) { + LOG.trace("no failure or Other branch found"); } } return (nextNode); } + protected SvcLogicRecorder getSvcLogicResource(String plugin) { + BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) + .getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(plugin); + if (sref != null) { + SvcLogicRecorder resourcePlugin = (SvcLogicRecorder) bctx + .getService(sref); + return resourcePlugin; + } + else { + return null; + } + } + } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ReleaseNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ReleaseNodeExecutor.java index 12e85db..b51cf9f 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ReleaseNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ReleaseNodeExecutor.java @@ -53,15 +53,7 @@ public class ReleaseNodeExecutor extends SvcLogicNodeExecutor { + plugin); } - BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) - .getBundleContext(); - - ServiceReference sref = bctx.getServiceReference(plugin); - - if (sref != null) { - SvcLogicResource resourcePlugin = (SvcLogicResource) bctx - .getService(sref); - + SvcLogicResource resourcePlugin = getSvcLogicResource(plugin); if (resourcePlugin != null) { try { @@ -85,10 +77,6 @@ public class ReleaseNodeExecutor extends SvcLogicNodeExecutor { LOG.warn("Could not find SvcLogicResource object for plugin " + plugin); } - } else { - LOG.warn("Could not find service reference object for plugin " - + plugin); - } SvcLogicNode nextNode = node.getOutcomeValue(outValue); if (nextNode != null) { @@ -112,5 +100,21 @@ public class ReleaseNodeExecutor extends SvcLogicNodeExecutor { return (nextNode); } + protected SvcLogicResource getSvcLogicResource(String plugin) { + BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) + .getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(plugin); + if (sref != null) { + SvcLogicResource resourcePlugin = (SvcLogicResource) bctx + .getService(sref); + return resourcePlugin; + } + else { + LOG.warn("Could not find service reference object for plugin " + plugin); + return null; + } + } + } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ReserveNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ReserveNodeExecutor.java index aa5d5ef..7db233f 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ReserveNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/ReserveNodeExecutor.java @@ -65,14 +65,9 @@ public class ReserveNodeExecutor extends SvcLogicNodeExecutor { + plugin); } - BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) - .getBundleContext(); - ServiceReference sref = bctx.getServiceReference(plugin); - if (sref != null) { - SvcLogicResource resourcePlugin = (SvcLogicResource) bctx - .getService(sref); + SvcLogicResource resourcePlugin = getSvcLogicResource(plugin); if (resourcePlugin != null) { @@ -96,10 +91,6 @@ public class ReserveNodeExecutor extends SvcLogicNodeExecutor { LOG.warn("Could not find SvcLogicResource object for plugin " + plugin); } - } else { - LOG.warn("Could not find service reference object for plugin " - + plugin); - } SvcLogicNode nextNode = node.getOutcomeValue(outValue); if (nextNode != null) { @@ -123,5 +114,21 @@ public class ReserveNodeExecutor extends SvcLogicNodeExecutor { return (nextNode); } + protected SvcLogicResource getSvcLogicResource(String plugin) { + BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) + .getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(plugin); + if (sref != null) { + SvcLogicResource resourcePlugin = (SvcLogicResource) bctx + .getService(sref); + return resourcePlugin; + } + else { + LOG.warn("Could not find service reference object for plugin " + plugin); + return null; + } + } + } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SaveNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SaveNodeExecutor.java index fc3adcc..f57eac8 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SaveNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SaveNodeExecutor.java @@ -93,14 +93,9 @@ public class SaveNodeExecutor extends SvcLogicNodeExecutor { + plugin); } - BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) - .getBundleContext(); - ServiceReference sref = bctx.getServiceReference(plugin); - if (sref != null) { - SvcLogicResource resourcePlugin = (SvcLogicResource) bctx - .getService(sref); + SvcLogicResource resourcePlugin = getSvcLogicResource(plugin); if (resourcePlugin != null) { @@ -125,10 +120,6 @@ public class SaveNodeExecutor extends SvcLogicNodeExecutor { LOG.warn("Could not find SvcLogicResource object for plugin " + plugin); } - } else { - LOG.warn("Could not find service reference object for plugin " - + plugin); - } SvcLogicNode nextNode = node.getOutcomeValue(outValue); if (nextNode != null) { @@ -151,4 +142,20 @@ public class SaveNodeExecutor extends SvcLogicNodeExecutor { return (nextNode); } + protected SvcLogicResource getSvcLogicResource(String plugin) { + BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) + .getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(plugin); + if (sref != null) { + SvcLogicResource resourcePlugin = (SvcLogicResource) bctx + .getService(sref); + return resourcePlugin; + } + else { + LOG.warn("Could not find service reference object for plugin " + plugin); + return null; + } + } + } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SetNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SetNodeExecutor.java index 0de109b..55ad737 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SetNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SetNodeExecutor.java @@ -173,7 +173,7 @@ public class SetNodeExecutor extends SvcLogicNodeExecutor { curEnt.getValue(), node, ctx); if (LOG.isDebugEnabled()) { - LOG.debug("Parameter value " + LOG.trace("Parameter value " + curEnt.getValue().asParsedExpr() + " resolves to " + curValue); LOG.debug("Setting context attribute " + lhsVarName diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicActivator.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicActivator.java index 33de964..691ad40 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicActivator.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicActivator.java @@ -69,6 +69,7 @@ public class SvcLogicActivator implements BundleActivator { put("set", new SetNodeExecutor()); put("switch", new SwitchNodeExecutor()); put("update", new UpdateNodeExecutor()); + put("break", new BreakNodeExecutor()); } }; diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicExpressionResolver.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicExpressionResolver.java index f248a59..29bd9b4 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicExpressionResolver.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicExpressionResolver.java @@ -65,12 +65,12 @@ public class SvcLogicExpressionResolver { if (atomType == AtomType.CONTEXT_VAR) { - LOG.debug("Evaluating context variable $"+varName); + LOG.trace("Evaluating context variable $"+varName); String varValue = ctx.getAttribute(varName); if (varValue == null) { - LOG.debug("Context variable $"+varName+" unset - treating as empty string"); + LOG.trace("Context variable $"+varName+" unset - treating as empty string"); varValue = ""; } @@ -78,7 +78,7 @@ public class SvcLogicExpressionResolver { } SvcLogicExpression parm = node.getParameter(varName); if (parm != null) { - LOG.debug("Evaluating value of parameter "+varName+": "+parm.asParsedExpr()); + LOG.trace("Evaluating value of parameter "+varName+": "+parm.asParsedExpr()); return (evaluate(parm, node, ctx)); } @@ -98,7 +98,7 @@ public class SvcLogicExpressionResolver { List operands = binExpr.getOperands(); if (operands.size() == 1) { - LOG.debug("SvcLogicBinaryExpression as no operator and one operand - evaluating its operand"); + LOG.trace("SvcLogicBinaryExpression as no operator and one operand - evaluating its operand"); return(evaluate(operands.get(0), node, ctx)); } else diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicServiceImpl.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicServiceImpl.java index 14366a9..6ee264e 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicServiceImpl.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SvcLogicServiceImpl.java @@ -268,6 +268,8 @@ public class SvcLogicServiceImpl implements SvcLogicService { LOG.info("Executing root node"); SvcLogicContext ctx = new SvcLogicContext(props); + ctx.setAttribute("currentGraph", graph.toString()); + ctx.setAttribute("X-ECOMP-RequestID", MDC.get("X-ECOMP-RequestID")); ctx.setDomDataBroker(domDataBroker); SvcLogicNode curNode = graph.getRootNode(); diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SwitchNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SwitchNodeExecutor.java index 4ea851f..2e32a58 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SwitchNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/SwitchNodeExecutor.java @@ -55,8 +55,7 @@ public class SwitchNodeExecutor extends SvcLogicNodeExecutor { if (LOG.isDebugEnabled()) { if (nextNode != null) { - LOG.debug("Next node to execute is node " - + nextNode.getNodeId()); + LOG.debug("Next node to execute is node " + nextNode.getNodeId()); } else { LOG.debug("No next node found"); } diff --git a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/UpdateNodeExecutor.java b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/UpdateNodeExecutor.java index ac1144c..e7a6621 100644 --- a/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/UpdateNodeExecutor.java +++ b/sli/provider/src/main/java/org/openecomp/sdnc/sli/provider/UpdateNodeExecutor.java @@ -87,14 +87,9 @@ public class UpdateNodeExecutor extends SvcLogicNodeExecutor { + plugin); } - BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) - .getBundleContext(); - ServiceReference sref = bctx.getServiceReference(plugin); + SvcLogicResource resourcePlugin = getSvcLogicResource(plugin); - if (sref != null) { - SvcLogicResource resourcePlugin = (SvcLogicResource) bctx - .getService(sref); if (resourcePlugin != null) { @@ -119,10 +114,6 @@ public class UpdateNodeExecutor extends SvcLogicNodeExecutor { LOG.warn("Could not find SvcLogicResource object for plugin " + plugin); } - } else { - LOG.warn("Could not find service reference object for plugin " - + plugin); - } SvcLogicNode nextNode = node.getOutcomeValue(outValue); if (nextNode != null) { @@ -145,4 +136,19 @@ public class UpdateNodeExecutor extends SvcLogicNodeExecutor { return (nextNode); } + protected SvcLogicResource getSvcLogicResource(String plugin) { + BundleContext bctx = FrameworkUtil.getBundle(this.getClass()) + .getBundleContext(); + + ServiceReference sref = bctx.getServiceReference(plugin); + if (sref != null) { + SvcLogicResource resourcePlugin = (SvcLogicResource) bctx + .getService(sref); + return resourcePlugin; + } + else { + LOG.warn("Could not find service reference object for plugin " + plugin); + return null; + } + } } diff --git a/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/BadPlugin.java b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/BadPlugin.java new file mode 100644 index 0000000..d1ab4cf --- /dev/null +++ b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/BadPlugin.java @@ -0,0 +1,56 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.provider; + +import java.util.Map; + +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicException; +import org.openecomp.sdnc.sli.SvcLogicJavaPlugin; + + +public class BadPlugin implements SvcLogicJavaPlugin { + public String selectLunch(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + String day = parameters.get("day"); + if (day == null || day.length() < 1) { + throw new SvcLogicException("What day is it?"); + } + switch (day) { + case ("monday"): { + return "pizza"; + } + case ("tuesday"): { + return "soup"; + } + case ("wednesday"): { + return "salad"; + } + case ("thursday"): { + return "sushi"; + } + case ("friday"): { + return "bbq"; + } + } + throw new SvcLogicException("Lunch cannot be served"); + } +} diff --git a/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/ExecuteNodeExecutorTest.java b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/ExecuteNodeExecutorTest.java new file mode 100644 index 0000000..3d43ee1 --- /dev/null +++ b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/ExecuteNodeExecutorTest.java @@ -0,0 +1,57 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.provider; + +import java.util.Map.Entry; + +import org.openecomp.sdnc.sli.DuplicateValueException; +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicException; +import org.openecomp.sdnc.sli.SvcLogicExpression; +import org.openecomp.sdnc.sli.SvcLogicGraph; +import org.openecomp.sdnc.sli.SvcLogicJavaPlugin; +import org.openecomp.sdnc.sli.SvcLogicNode; + +import junit.framework.TestCase; + +public class ExecuteNodeExecutorTest extends TestCase { + public class MockExecuteNodeExecutor extends ExecuteNodeExecutor { + + protected SvcLogicJavaPlugin getSvcLogicJavaPlugin(String pluginName) { + return (SvcLogicJavaPlugin) new LunchSelectorPlugin(); + } + + protected String evaluate(SvcLogicExpression expr, SvcLogicNode node, + SvcLogicContext ctx) throws SvcLogicException { + return "selectLunch"; + } + } + + public void testBadPlugin() throws DuplicateValueException, SvcLogicException { + LunchSelectorPlugin p = new LunchSelectorPlugin(); + MockExecuteNodeExecutor execute = new MockExecuteNodeExecutor(); + SvcLogicNode node = new SvcLogicNode(0, "", "", new SvcLogicGraph()); + node.setAttribute("method", "selectLunch"); + execute.execute(new SvcLogicServiceImpl(), new SvcLogicNode(0, "", "", new SvcLogicGraph()), new SvcLogicContext()); + } + +} diff --git a/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/LunchSelectorPlugin.java b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/LunchSelectorPlugin.java new file mode 100644 index 0000000..b9156bc --- /dev/null +++ b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/LunchSelectorPlugin.java @@ -0,0 +1,78 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.provider; + +import java.util.Map; + +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicException; +import org.openecomp.sdnc.sli.SvcLogicJavaPlugin; + + + +public class LunchSelectorPlugin implements SvcLogicJavaPlugin { + public class UnknownLunchDayException extends Exception{ + + public UnknownLunchDayException(String string) { + super(string); + } + + } + class Sandwhich { + String meat; + String cheese; + + public Sandwhich(String meat, String cheese) { + this.meat = meat; + this.cheese = cheese; + } + } + + public String selectLunch(Map parameters, SvcLogicContext ctx) throws Exception { + String day = parameters.get("day"); + if (day == null || day.length() < 1) { + throw new UnknownLunchDayException("What day is it?"); + } + switch (day) { + case ("monday"): { + return "pizza"; + } + case ("tuesday"): { + return "soup"; + } + case ("wednesday"): { + return "salad"; + } + case ("thursday"): { + return "sushi"; + } + case ("friday"): { + return "bbq"; + } + } + throw new SvcLogicException("Lunch cannot be served"); + } + + public Sandwhich makeLunch(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + return new Sandwhich("ham", "american"); + } +} diff --git a/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/MdsalHelperTest.java b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/MdsalHelperTest.java new file mode 100644 index 0000000..a4e41bb --- /dev/null +++ b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/MdsalHelperTest.java @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.provider; + +import junit.framework.TestCase; + +public class MdsalHelperTest extends TestCase { + + public static final String pathToSdnPropertiesFile = "./src/test/resources/l3sdn.properties"; + + public void testSdnProperties() { + MdsalHelperTesterUtil.loadProperties(pathToSdnPropertiesFile); + assertEquals("synccomplete", MdsalHelperTesterUtil.mapEnumeratedValue("request-status", "Synccomplete")); + assertEquals("asynccomplete", MdsalHelperTesterUtil.mapEnumeratedValue("request-status", "asynccomplete")); + assertEquals("notifycomplete", MdsalHelperTesterUtil.mapEnumeratedValue("request-status", "notifycomplete")); + assertEquals("service-configuration-operation", MdsalHelperTesterUtil.mapEnumeratedValue("rpc-name", + "ServiceConfigurationOperation")); + } + + public void testNegativeSdnProperties() { + assertNotSame("synccomplete", MdsalHelperTesterUtil.mapEnumeratedValue("request-status", "Synccomplete")); + } + +} diff --git a/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/MdsalHelperTesterUtil.java b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/MdsalHelperTesterUtil.java new file mode 100644 index 0000000..01e333f --- /dev/null +++ b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/MdsalHelperTesterUtil.java @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.provider; + +import org.openecomp.sdnc.sli.provider.MdsalHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MdsalHelperTesterUtil extends MdsalHelper { + + private static final Logger LOG = LoggerFactory.getLogger(MdsalHelperTesterUtil.class); + + //Normally static init of classes goes here for some weird classloader thing + static { + String str = "Hello World!"; + } + +} diff --git a/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/PluginTest.java b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/PluginTest.java new file mode 100644 index 0000000..035cd3e --- /dev/null +++ b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/PluginTest.java @@ -0,0 +1,106 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.provider; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicGraph; +import org.openecomp.sdnc.sli.SvcLogicJavaPlugin; +import org.openecomp.sdnc.sli.SvcLogicNode; + +import junit.framework.TestCase; + +public class PluginTest extends TestCase { + + // The existing plugins work just like a VoidDummyPlugin + // They will return null simply because they are all void + // The attribute emitsOutcome will not be present, the expected outcome is success when no exception is thrown by the plugin + public void testOldPlugin() throws Exception { + ExecuteNodeExecutor executor = new ExecuteNodeExecutor(); + SvcLogicJavaPlugin plugin = new VoidDummyPlugin(); + + Class pluginClass = plugin.getClass(); + Method pluginMethod = pluginClass.getMethod("dummy", Map.class, SvcLogicContext.class); + Map parmMap = new HashMap(); + SvcLogicContext ctx = new SvcLogicContext(); + Object o = pluginMethod.invoke(plugin, parmMap, ctx); + + SvcLogicGraph graph = new SvcLogicGraph(); + SvcLogicNode node = new SvcLogicNode(1, "return", graph); + String emitsOutcome = SvcLogicExpressionResolver.evaluate(node.getAttribute("emitsOutcome"), node, ctx); + String outValue = executor.mapOutcome(o, emitsOutcome); + assertEquals("success",outValue); + } + + //Newer plugins can set the attribute emitsOutcome to true, if so they should return a string + //The string represents the outcome value + public void testNewPlugin() throws Exception { + ExecuteNodeExecutor executor = new ExecuteNodeExecutor(); + SvcLogicJavaPlugin plugin = new LunchSelectorPlugin(); + + Class pluginClass = plugin.getClass(); + Method pluginMethod = pluginClass.getMethod("selectLunch", Map.class, SvcLogicContext.class); + + Map parmMap = new HashMap(); + SvcLogicContext ctx = new SvcLogicContext(); + + parmMap.put("day", "monday"); + Object o = pluginMethod.invoke(plugin, parmMap, ctx); + SvcLogicGraph graph = new SvcLogicGraph(); + SvcLogicNode node = new SvcLogicNode(1, "return", graph); + node.setAttribute("emitsOutcome", "true"); + String emitsOutcome = SvcLogicExpressionResolver.evaluate(node.getAttribute("emitsOutcome"), node, ctx); + String outValue = executor.mapOutcome(o, emitsOutcome); + assertEquals("pizza", outValue); + + parmMap.put("day", "tuesday"); + outValue = (String) pluginMethod.invoke(plugin, parmMap, ctx); + o = pluginMethod.invoke(plugin, parmMap, ctx); + outValue = executor.mapOutcome(o, emitsOutcome); + assertEquals("soup",outValue); + + } + + //APPC had some legacy plugins returning objects which should not be treated as outcomes + //The attribute emitsOutcome will not be set + //The outcome should be success as it has always been + public void testObjPlugin() throws Exception{ + ExecuteNodeExecutor executor = new ExecuteNodeExecutor(); + SvcLogicJavaPlugin plugin = new LunchSelectorPlugin(); + + Class pluginClass = plugin.getClass(); + Method pluginMethod = pluginClass.getMethod("makeLunch", Map.class, SvcLogicContext.class); + + Map parmMap = new HashMap(); + SvcLogicContext ctx = new SvcLogicContext(); + Object o = pluginMethod.invoke(plugin, parmMap, ctx); + SvcLogicGraph graph = new SvcLogicGraph(); + SvcLogicNode node = new SvcLogicNode(1, "return", graph); + String emitsOutcome = SvcLogicExpressionResolver.evaluate(node.getAttribute("emitsOutcome"), node, ctx); + String outValue = executor.mapOutcome(o, emitsOutcome); + assertEquals("success",outValue); + } + +} diff --git a/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/VoidDummyPlugin.java b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/VoidDummyPlugin.java new file mode 100644 index 0000000..6c8214a --- /dev/null +++ b/sli/provider/src/test/java/org/openecomp/sdnc/sli/provider/VoidDummyPlugin.java @@ -0,0 +1,38 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.provider; + +import java.util.Map; + +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicException; +import org.openecomp.sdnc.sli.SvcLogicJavaPlugin; + + + +public class VoidDummyPlugin implements SvcLogicJavaPlugin { + + public void dummy(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + return; + } + +} diff --git a/sliPluginUtils/provider/pom.xml b/sliPluginUtils/provider/pom.xml index 093d585..739f89d 100755 --- a/sliPluginUtils/provider/pom.xml +++ b/sliPluginUtils/provider/pom.xml @@ -51,6 +51,12 @@ commons-lang3 3.1 + + org.hamcrest + hamcrest-library + 1.3 + test + diff --git a/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/DME2.java b/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/DME2.java new file mode 100644 index 0000000..0acaf5f --- /dev/null +++ b/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/DME2.java @@ -0,0 +1,110 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.SliPluginUtils; + +import java.util.Map; + +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicException; +import org.openecomp.sdnc.sli.SvcLogicJavaPlugin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + + +/** + * A SvcLogicJavaPlugin that generates DME2 proxy urls using parameters from context memory. + */ +public class DME2 implements SvcLogicJavaPlugin { + String aafUserName; + String aafPassword; + String envContext; + String routeOffer; + String[] proxyUrls; + Integer index; + String commonServiceVersion; + String partner; + + private static final Logger LOG = LoggerFactory.getLogger(DME2.class); + + public void setPartner(String partner) { + if (partner != null && partner.length() > 0) { + this.partner = partner; + } + } + + public DME2(String aafUserName, String aafPassword, String envContext, String routeOffer, String[] proxyUrls, String commonServiceVersion) { + this.aafUserName = aafUserName; + this.aafPassword = aafPassword; + this.envContext = envContext; + this.routeOffer = routeOffer; + this.proxyUrls = proxyUrls; + this.index = 0; + this.commonServiceVersion = commonServiceVersion; + } + + // constructs a URL to contact the proxy which contacts a DME2 service + public String constructUrl(String service, String version, String subContext) { + StringBuilder sb = new StringBuilder(); + + // The hostname is assigned in a round robin fashion + sb.append(acquireHostName()); + sb.append("/service=" + service); + + //If the directedGraph passes an explicit version use that, if not use the commonServiceVersion found in the properties file + if (version == null) { + version = this.commonServiceVersion; + } + sb.append("/version=" + version); + + sb.append("/envContext=" + this.envContext); + if (this.routeOffer != null && this.routeOffer.length() > 0) { + sb.append("/routeOffer=" + this.routeOffer); + } + if (subContext != null && subContext.length() > 0) { + sb.append("/subContext=" + subContext); + } + sb.append("?dme2.password=" + this.aafPassword); + sb.append("&dme2.username=" + this.aafUserName); + if (this.partner != null) { + sb.append("&dme2.partner=" + this.partner); + } + return (sb.toString()); + } + + public synchronized String acquireHostName() { + String retVal = proxyUrls[index]; + index++; + if (index == this.proxyUrls.length) { + index = 0; + } + return retVal; + } + + // Node entry point + public void constructUrl(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + SliPluginUtils.checkParameters(parameters, new String[] { "service", "outputPath" }, LOG); + String completeProxyUrl = constructUrl(parameters.get("service"), parameters.get("version"), parameters.get("subContext")); + ctx.setAttribute(parameters.get("outputPath"), completeProxyUrl); + } + +} diff --git a/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils.java b/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils.java index ea8df51..1dd29a5 100644 --- a/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils.java +++ b/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils.java @@ -21,12 +21,18 @@ package org.openecomp.sdnc.sli.SliPluginUtils; +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Properties; +import java.util.Set; import java.util.UUID; import org.apache.commons.lang3.StringUtils; @@ -43,582 +49,706 @@ import org.slf4j.LoggerFactory; * @see org.openecomp.sdnc.sli.SvcLogicContext */ public class SliPluginUtils implements SvcLogicJavaPlugin { - public enum LogLevel { - TRACE, DEBUG, INFO, WARN, ERROR; - } - - private static final Logger LOG = LoggerFactory.getLogger(SliPluginUtils.class); - - - // ========== CONSTRUCTORS ========== - - public SliPluginUtils() {} - - public SliPluginUtils( Properties props ) {} - - - - // ========== CONTEXT MEMORY FUNCTIONS ========== + public enum LogLevel { + TRACE, DEBUG, INFO, WARN, ERROR; + } + + private static final Logger LOG = LoggerFactory.getLogger(SliPluginUtils.class); + + + // ========== CONSTRUCTORS ========== + + public SliPluginUtils() {} + + public SliPluginUtils( Properties props ) {} + + + + // ========== CONTEXT MEMORY FUNCTIONS ========== + + /** + * Removes 1 or more elements from a list in context memory. + *

+ * Values are removed based on either the index in the list, a key-value + * pair, or a list of key-value pairs that all must match in the element. + * @param parameters + * @param ctx Reference to context memory + * @throws SvcLogicException All exceptions are wrapped in + * SvcLogicException for compatibility with SLI. + * @since 7.0.1 + */ + public void ctxListRemove( Map parameters, SvcLogicContext ctx ) throws SvcLogicException { + try{ + LOG.debug( "ENTERING Execute Node \"ctxListRemove\"" ); + + // Validate, Log, & read parameters + checkParameters(parameters, new String[]{"list_pfx"}, LOG); + logExecuteNodeParameters(parameters, LOG, LogLevel.DEBUG); + String list_pfx = parameters.get("list_pfx"); + String param_index = parameters.get("index"); + String param_key = parameters.get("key"); + String param_value = parameters.get("value"); + String param_keys_length = parameters.get("keys_length"); + + // Initialize context memory list mimic + SvcLogicContextList list; + + // Process based on input parameters: + // index: remove object at specific index + // key & value: remove all objects with key-value pair + // keys_length: remove all objects that match all key-value pairs + // in list + if( param_index != null ) { + // Parse index + LOG.trace("executing remove by index logic"); + int index; + try { + index = Integer.parseInt(param_index); + } + catch( NumberFormatException e ) { + throw new IllegalArgumentException("\"index\" parameter is not a number. index = " + param_index, e); + } + + // Extract list from context memory & remove object @ index + LOG.trace("extracting list from context memory"); + list = SvcLogicContextList.extract(ctx, list_pfx); + LOG.trace("removing elements from list"); + list.remove(index); + } + else if( param_value != null ) { + if( param_key == null ) { param_key = ""; } + + // Extract list from context memory & remove objects with + // key-value pair + LOG.trace("executing remove by key-value pair logic"); + LOG.trace("extracting list from context memory"); + list = SvcLogicContextList.extract(ctx, list_pfx); + LOG.trace("removing elements from list"); + list.remove( param_key, param_value ); + } + else if( param_keys_length != null ) { + // Parse keys_length + LOG.trace("executing remove by key-value pair list logic"); + int keys_length; + try { + keys_length = Integer.parseInt(param_keys_length); + } + catch( NumberFormatException e ) { + throw new IllegalArgumentException("\"keys_length\" parameters is not a number. keys_length = " + param_keys_length, e); + } + + // Obtain key-value pairs to check from parameters + LOG.trace("reading keys parameter list"); + HashMap keys_values = new HashMap(); + for( int i = 0; i < keys_length; i++ ) { + keys_values.put(parameters.get("keys[" + i + "].key"), parameters.get("keys[" + i + "].value")); + } + + // Extract list from context memory & remove objects with all + // key-value pairs matching + LOG.trace("extracting list from context memory"); + list = SvcLogicContextList.extract(ctx, list_pfx); + LOG.trace("removing elements from list"); + list.remove(keys_values); + } + else { + throw new IllegalArgumentException("Required parameters missing. Requires one of: index, key & value, or keys_length array"); + } + + // Remove index from list + LOG.trace("writing list back into context memory"); + list.writeToContext(ctx); + } + catch( Exception e ) { + throw new SvcLogicException( "An error occurred in the ctxListRemove Execute node", e ); + } + finally { + LOG.debug( "EXITING Execute Node \"ctxListRemove\"" ); + } + } /** - * Removes 1 or more elements from a list in context memory. - *

- * Values are removed based on either the index in the list, a key-value - * pair, or a list of key-value pairs that all must match in the element. - * @param parameters + * ctxSortList + * @param parameters - the set of required parameters must contain list and delimiter. * @param ctx Reference to context memory - * @throws SvcLogicException All exceptions are wrapped in - * SvcLogicException for compatibility with SLI. - * @since 7.0.1 + * @throws SvcLogicException if a required parameter is missing an exception is thrown */ - public void ctxListRemove( Map parameters, SvcLogicContext ctx ) throws SvcLogicException { - try{ - LOG.debug( "ENTERING Execute Node \"ctxListRemove\"" ); - - // Validate, Log, & read parameters - checkParameters(parameters, new String[]{"list_pfx"}, LOG); - logExecuteNodeParameters(parameters, LOG, LogLevel.DEBUG); - String list_pfx = parameters.get("list_pfx"); - String param_index = parameters.get("index"); - String param_key = parameters.get("key"); - String param_value = parameters.get("value"); - String param_keys_length = parameters.get("keys_length"); - - // Initialize context memory list mimic - SvcLogicContextList list; - - // Process based on input parameters: - // index: remove object at specific index - // key & value: remove all objects with key-value pair - // keys_length: remove all objects that match all key-value pairs - // in list - if( param_index != null ) { - // Parse index - LOG.trace("executing remove by index logic"); - int index; - try { - index = Integer.parseInt(param_index); - } - catch( NumberFormatException e ) { - throw new IllegalArgumentException("\"index\" parameter is not a number. index = " + param_index, e); - } - - // Extract list from context memory & remove object @ index - LOG.trace("extracting list from context memory"); - list = SvcLogicContextList.extract(ctx, list_pfx); - LOG.trace("removing elements from list"); - list.remove(index); - } - else if( param_value != null ) { - if( param_key == null ) { param_key = ""; } - - // Extract list from context memory & remove objects with - // key-value pair - LOG.trace("executing remove by key-value pair logic"); - LOG.trace("extracting list from context memory"); - list = SvcLogicContextList.extract(ctx, list_pfx); - LOG.trace("removing elements from list"); - list.remove( param_key, param_value ); - } - else if( param_keys_length != null ) { - // Parse keys_length - LOG.trace("executing remove by key-value pair list logic"); - int keys_length; - try { - keys_length = Integer.parseInt(param_keys_length); - } - catch( NumberFormatException e ) { - throw new IllegalArgumentException("\"keys_length\" parameters is not a number. keys_length = " + param_keys_length, e); - } - - // Obtain key-value pairs to check from parameters - LOG.trace("reading keys parameter list"); - HashMap keys_values = new HashMap<>(); - for( int i = 0; i < keys_length; i++ ) { - keys_values.put(parameters.get("keys[" + i + "].key"), parameters.get("keys[" + i + "].value")); - } - - // Extract list from context memory & remove objects with all - // key-value pairs matching - LOG.trace("extracting list from context memory"); - list = SvcLogicContextList.extract(ctx, list_pfx); - LOG.trace("removing elements from list"); - list.remove(keys_values); - } - else { - throw new IllegalArgumentException("Required parameters missing. Requires one of: index, key & value, or keys_length array"); - } - - // Remove index from list - LOG.trace("writing list back into context memory"); - list.writeToContext(ctx); - } - catch( Exception e ) { - throw new SvcLogicException( "An error occurred in the ctxListRemove Execute node", e ); - } - finally { - LOG.debug( "EXITING Execute Node \"ctxListRemove\"" ); - } - } - - // TODO: javadoc - public void ctxSortList( Map parameters, SvcLogicContext ctx ) throws SvcLogicException { - checkParameters(parameters, new String[]{"list","delimiter"}, LOG); - ArrayList list = new ArrayList<>(); - - String[] sort_fields = null; - if( parameters.containsKey("sort-fields") ) { - sort_fields = parameters.get("sort-fields").split(parameters.get("delimiter"), 0); - } - - String ctx_list_str = parameters.get("list"); - int listSz = getArrayLength(ctx, ctx_list_str); - - - - for( int i = 0; i < listSz; i++ ) { - list.add( new SortableCtxListElement(ctx, ctx_list_str + '[' + i + ']', sort_fields) ); - } - Collections.sort(list); - - ctxBulkErase(ctx, ctx_list_str); - int i = 0; - for( SortableCtxListElement list_element : list ) { - for( Map.Entry entry : list_element.child_elements.entrySet() ) { - if( sort_fields == null ) { - ctx.setAttribute(ctx_list_str + '[' + i + ']', entry.getValue()); - } - else { - ctx.setAttribute(ctx_list_str + '[' + i + "]." + entry.getKey(), entry.getValue()); - } - } - i++; - } - // Reset list length (removed by ctxBulkErase above) - ctx.setAttribute(ctx_list_str+"_length", ""+listSz); - } - - // TODO: javadoc - public void generateUUID( Map parameters, SvcLogicContext ctx ) throws SvcLogicException { - checkParameters(parameters, new String[]{"ctx-destination"}, LOG); - ctx.setAttribute(parameters.get("ctx-destination"), UUID.randomUUID().toString() ); - } + public void ctxSortList( Map parameters, SvcLogicContext ctx ) throws SvcLogicException { + checkParameters(parameters, new String[]{"list","delimiter"}, LOG); + ArrayList list = new ArrayList(); + + String[] sort_fields = null; + if( parameters.containsKey("sort-fields") ) { + sort_fields = parameters.get("sort-fields").split(parameters.get("delimiter"), 0); + } + + String ctx_list_str = parameters.get("list"); + int listSz = getArrayLength(ctx, ctx_list_str); + + + + for( int i = 0; i < listSz; i++ ) { + list.add( new SortableCtxListElement(ctx, ctx_list_str + '[' + i + ']', sort_fields) ); + } + Collections.sort(list); + + ctxBulkErase(ctx, ctx_list_str); + int i = 0; + for( SortableCtxListElement list_element : list ) { + for( Map.Entry entry : list_element.child_elements.entrySet() ) { + if( sort_fields == null ) { + ctx.setAttribute(ctx_list_str + '[' + i + ']', entry.getValue()); + } + else { + ctx.setAttribute(ctx_list_str + '[' + i + "]." + entry.getKey(), entry.getValue()); + } + } + i++; + } + // Reset list length (removed by ctxBulkErase above) + ctx.setAttribute(ctx_list_str+"_length", ""+listSz); + } /** - * Provides substring functionality to Directed Graphs. - *

- * Calls either String.substring(String beginIndex) or - * String.substring(String beginInded, String endIndex) if the end-index - * is present or not. - * @param parameters HashMap of parameters passed by the DG to this function - * - * - * - * - * - * - * - * - *
parameterMandatory/Optionaldescription
stringMandatoryString to perform substring on
resultMandatoryKey in context memory to populate the resulting string in
begin-indexMandatoryBeginning index to pass to Java substring function
end-indexOptionalEnding index to pass to Java substring function. If not included, String.substring(begin) will be called.
+ * generates a UUID and writes it to context memory + * @param parameters - ctx-destination is a required parameter * @param ctx Reference to context memory - * @throws SvcLogicException - * @since 8.0.1 + * @throws SvcLogicException thrown if a UUID cannot be generated or if ctx-destination is missing or null */ - public void substring( Map parameters, SvcLogicContext ctx ) throws SvcLogicException { - try { - checkParameters( parameters, new String[]{"string","begin-index","result"}, LOG ); - final String string = parameters.get("string"); - final String result = parameters.get("result"); - final String begin = parameters.get("begin-index"); - final String end = parameters.get("end-index"); - - if( StringUtils.isEmpty(end) ) { - ctx.setAttribute( result, string.substring(Integer.parseInt(begin)) ); - } - else { - ctx.setAttribute( result, string.substring(Integer.parseInt(begin), Integer.parseInt(end)) ); - } + public void generateUUID( Map parameters, SvcLogicContext ctx ) throws SvcLogicException { + checkParameters(parameters, new String[]{"ctx-destination"}, LOG); + ctx.setAttribute(parameters.get("ctx-destination"), UUID.randomUUID().toString() ); + } + + /** + * Provides substring functionality to Directed Graphs. + *

+ * Calls either String.substring(String beginIndex) or + * String.substring(String beginInded, String endIndex) if the end-index + * is present or not. + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
stringMandatoryString to perform substring on
resultMandatoryKey in context memory to populate the resulting string in
begin-indexMandatoryBeginning index to pass to Java substring function
end-indexOptionalEnding index to pass to Java substring function. If not included, String.substring(begin) will be called.
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 8.0.1 + * @see SliPluginUtils#substring(Map, SvcLogicContext) + */ + @Deprecated + public void substring( Map parameters, SvcLogicContext ctx ) throws SvcLogicException { + try { + checkParameters( parameters, new String[]{"string","begin-index","result"}, LOG ); + final String string = parameters.get("string"); + final String result = parameters.get("result"); + final String begin = parameters.get("begin-index"); + final String end = parameters.get("end-index"); + + if( StringUtils.isEmpty(end) ) { + ctx.setAttribute( result, string.substring(Integer.parseInt(begin)) ); + } + else { + ctx.setAttribute( result, string.substring(Integer.parseInt(begin), Integer.parseInt(end)) ); + } + } + catch( Exception e ) { + throw new SvcLogicException( "An error occurred while the Directed Graph was performing a substring", e ); + } + } + + + + // ========== PUBLIC STATIC UTILITY FUNCTIONS ========== + + /** + * Throws an exception and writes an error to the log file if a required + * parameters is not found in the parametersMap. + *

+ * Use at the beginning of functions that can be called by Directed Graphs + * and can take parameters to verify that all parameters have been provided + * by the Directed Graph. + * @param parametersMap parameters Map passed to this node + * @param requiredParams Array of parameters required by the calling function + * @param log Reference to Logger to log to + * @throws SvcLogicException if a String in the requiredParams array is + * not a key in parametersMap. + * @since 1.0 + */ + public static final void checkParameters(Map parametersMap, String[] requiredParams, Logger log) throws SvcLogicException { + if( requiredParams == null || requiredParams.length < 1){ + log.debug("required parameters was empty, exiting early."); + return; } - catch( Exception e ) { - throw new SvcLogicException( "An error occurred while the Directed Graph was performing a substring", e ); + if (parametersMap == null || parametersMap.keySet().size() < 1){ + String errorMessage = "This method requires the parameters [" + StringUtils.join(requiredParams,",") + "], but no parameters were passed in."; + log.error(errorMessage); + throw new SvcLogicException(errorMessage); } - } - - - // ========== PUBLIC STATIC UTILITY FUNCTIONS ========== - - /** - * Throws an exception and writes an error to the log file if a required - * parameters is not found in the parametersMap. - *

- * Use at the beginning of functions that can be called by Directed Graphs - * and can take parameters to verify that all parameters have been provided - * by the Directed Graph. - * @param parametersMap parameters Map passed to this node - * @param requiredParams Array of parameters required by the calling function - * @param log Reference to Logger to log to - * @throws SvcLogicException if a String in the requiredParams array is - * not a key in parametersMap. - * @since 1.0 - */ - public static final void checkParameters( Map parametersMap, String[] requiredParams, Logger log ) throws SvcLogicException { - for( String param : requiredParams ) { - if( !parametersMap.containsKey(param) ) { - log.error("Required parameter \"" + param + "\" was not found in parameter list."); - throw new SvcLogicException("Required parameter \"" + param + "\" was not found in parameter list"); - } + for (String param : requiredParams) { + if (!parametersMap.containsKey(param)) { + String errorMessage = "Required parameter \"" + param + "\" was not found in parameter list."; + log.error(errorMessage); + log.error("Total list of required parameters is [" + StringUtils.join(requiredParams, ",") + "]."); + throw new SvcLogicException(errorMessage); + } + } } - } - /** - * Removes all key-value pairs with keys that begin with pfx + /** + * Removes all key-value pairs with keys that begin with pfx + * @param ctx Reference to context memory + * @param pfx Prefix of key-value pairs to remove + * @since 1.0 + */ + public static final void ctxBulkErase( SvcLogicContext ctx, String pfx ) { + ArrayList Keys = new ArrayList( ctx.getAttributeKeySet() ); + for( String key : Keys ) { + if( key.startsWith( pfx ) ) { + ctx.setAttribute( pfx + key.substring(pfx.length()) , null); + } + } + } + + /** + * Copies all context memory key-value pairs that start with src_pfx to + * the keys that start with dest_pfx + suffix, where suffix is the result + * of {@code key.substring(src_pfx.length())}. + *

+ * Does NOT guarantee removal of all keys at the destination before + * copying, but will overwrite any destination keys that have a + * corresponding source key. Use {@link #ctxBulkErase(SvcLogicContext, String) ctxBulkErase} + * before copy to erase destination root before copying from source. + * @param ctx Reference to context memory. + * @param src_pfx Prefix of the keys to copy values from. + * @param dest_pfx Prefix of the keys to copy values to. + * @since 1.0 + */ + public static final void ctxBulkCopy( SvcLogicContext ctx, String src_pfx, String dest_pfx ) { + // Remove trailing period from dest_pfx + if( dest_pfx.charAt(dest_pfx.length()-1) == '.' ) { + dest_pfx = dest_pfx.substring(0,dest_pfx.length()-1); + } + + // For each context key that begins with src_pfx, set the value of the + // key dest_pfx + the suffix of the key to the key's value + ArrayList Keys = new ArrayList(ctx.getAttributeKeySet()); + for( String key : Keys ) { + if( key.startsWith(src_pfx) ) { + // Get suffix (no leading period) + String suffix = key.substring(src_pfx.length()); + if( suffix.charAt(0) == '.') { + suffix = suffix.substring(1); + } + + // Set destination's value to key's value + ctx.setAttribute(dest_pfx + '.' + suffix, ctx.getAttribute(key)); + } + } + } + + /** + * Creates and returns a {@code Map} that is a subset of + * context memory where all keys begin with the prefix. + * @param ctx Reference to context memory. + * @param prefix Returned map's keys should all begin with this value. + * @return A {@code Map} containing all the key-value pairs + * in ctx whose key begins with prefix. + */ + public static final Map ctxGetBeginsWith( SvcLogicContext ctx, String prefix ) { + Map prefixMap = new HashMap(); + + for( String key : ctx.getAttributeKeySet() ) { + if( key.startsWith(prefix) ) { + prefixMap.put( key, ctx.getAttribute(key) ); + } + } + + return prefixMap; + } + + /** + * Returns true if key's value in context memory is "" or if it doesn't + * exist in context memory. + * @param ctx Reference to context memory. + * @param key Key to search for. + * @return true if key's value in context memory is "" or if it doesn't + * exist in context memory. + * @since 1.0 + */ + public static final boolean ctxKeyEmpty( SvcLogicContext ctx, String key ) { + String value = ctx.getAttribute(key); + return value == null || value.isEmpty(); + } + + /** + * Adds all key-value pairs in the entries Map to context memory. + * @param ctx Reference to context memory. Value's {@code toString()} + * function is used to add it. + * @param entries {@code Map} of key-value pairs to add to + * context memory. Value's {@code toString()} function is used to add it. + * @return Reference to context memory to be used for function chaining. + */ + public static final SvcLogicContext ctxPutAll( SvcLogicContext ctx, Map entries ) { + for( Map.Entry entry : entries.entrySet() ) { + ctxSetAttribute( ctx, entry.getKey(), entry.getValue() ); + //ctx.setAttribute(entry.getKey(), entry.getValue().toString()); + } + + return ctx; + } + + /** + * Sets a key in context memory to the output of object's toString(). The + * key is deleted from context memory if object is null. + * @param ctx Reference to context memory. + * @param key Key to set. + * @param object Object whose toString() will be the value set + */ + public static final void ctxSetAttribute( SvcLogicContext ctx, String key, Object object ) { + if( object == null ) { + ctx.setAttribute(key, null); + } + else { + ctx.setAttribute(key, object.toString()); + } + } + + /** + * Sets a key in context memory to the output of object's toString(). + *

+ * The key is deleted from context memory if object is null. The key and + * value set in context memory are logged to the Logger at the provided + * logLevel level. + * @param Any Java object + * @param ctx Reference to context memory. + * @param key Key to set. + * @param obj Object whose toString() will be the value set + * @param LOG Logger to log to + * @param logLevel level to log at in Logger + */ + public static final void ctxSetAttribute( SvcLogicContext ctx, String key, O obj, Logger LOG, LogLevel logLevel ) { + String value = Objects.toString( obj, null ); + ctx.setAttribute( key, value ); + if( logLevelIsEnabled(LOG, logLevel ) ) { + if( value == null ) { + logMessageAtLevel( LOG, logLevel, "Deleting " + key ); + } + else { + logMessageAtLevel( LOG, logLevel, "Setting " + key + " = " + value ); + } + } + } + + /** + * Utility function used to get an array's length from context memory. + * Will return 0 if key doesn't exist in context memory or isn't numeric. + *

+ * Use to obtain a context memory array length without having to worry + * about throwing a NumberFormatException. + * @param ctx Reference to context memory + * @param key Key in context memory whose value is the array's length. If + * the key doesn't end in "_length", then "_length is appended. + * @param log Reference to Logger to log to + * @return The array length or 0 if the key is not found in context memory. + * @since 1.0 + */ + public static final int getArrayLength( SvcLogicContext ctx, String key ) { + return getArrayLength(ctx, key, null, null, null); + } + + /** + * Utility function used to get an array's length from context memory. + * Will return 0 if key doesn't exist in context memory or isn't numeric + * and print the provided log message to the configured log file. + *

+ * Use to obtain a context memory array length without having to worry + * about throwing a NumberFormatException. + * @param ctx Reference to context memory. + * @param key Key in context memory whose value is the array's length. If + * the key doesn't end in "_length", then "_length is appended. + * @param log Reference to Logger to log to. Doesn't log if null. + * @param logLevel Logging level to log the message at if the context + * memory key isn't found. Doesn't log if null. + * @param log_message Message to log if the context memory key isn't found. + * Doesn't log if null. + * @return The array length or 0 if the key is not found in context memory. + * @since 1.0 + */ + public static final int getArrayLength( SvcLogicContext ctx, String key, Logger log, LogLevel logLevel, String log_message ) { + String ctxKey = ( key.endsWith("_length") ) ? key : key + "_length"; + try { + return Integer.parseInt(ctx.getAttribute(ctxKey)); + } + catch( NumberFormatException e ) { + if( log != null && logLevel != null && log_message != null ) { + switch( logLevel ) { + case TRACE: + log.trace(log_message); + case DEBUG: + log.debug(log_message); + break; + case INFO: + log.info(log_message); + break; + case WARN: + log.warn(log_message); + break; + case ERROR: + log.error(log_message); + break; + } + } + } + + return 0; + } + + /** + * Prints sorted context memory key-value pairs to the log file at the log + * level. Returns immediately if the log level isn't enabled. + *

+ * O(n log(n)) time where n = size of context memory + * @param ctx Reference to context memory + * @param log Reference to Logger to log to + * @param logLevel Logging level to log the context memory key-value pairs + * at. + * @since 1.0 + */ + public static final void logContextMemory( SvcLogicContext ctx, Logger log, LogLevel logLevel ) { + logLevelIsEnabled( log, logLevel ); + + // Print sorted context memory key-value pairs to the log + ArrayList keys = new ArrayList(ctx.getAttributeKeySet()); + Collections.sort(keys); + for( String key : keys ) { + logMessageAtLevel( log, logLevel, key + " = " + ctx.getAttribute(key) ); + } + } + + + + // ========== PRIVATE FUNCTIONS ========== + + // TODO: javadoc + /** + * + * @param parameters + * @param log + * @param loglevel + * @since 7.0.1 + */ + public static final void logExecuteNodeParameters( Map parameters, Logger log, LogLevel loglevel ) { + logLevelIsEnabled( log, loglevel ); + + for( Map.Entry param : parameters.entrySet() ) { + logMessageAtLevel( log, loglevel, "PARAM: " + param.getKey() + " = " + param.getValue() ); + } + } + + // TODO: javadoc + /** + * Returns true if the loglevel is enabled. Otherwise, returns false. + * @param log Reference to logger + * @param loglevel Log level to check if enabled + * @return True if the loglevel is enabled. Otherwise, false + * @since 7.0.1 + */ + private static final boolean logLevelIsEnabled( Logger log, LogLevel loglevel ) { + // Return immediately if logging level isn't enabled + switch( loglevel ) { + case TRACE: + if( log.isTraceEnabled() ) { return true; } + return false; + case DEBUG: + if( log.isDebugEnabled() ) { return true; } + return false; + case INFO: + if( log.isInfoEnabled() ) { return true; } + return false; + case WARN: + if( log.isWarnEnabled() ) { return true; } + return false; + case ERROR: + if( log.isErrorEnabled() ) { return true; } + return false; + default: + throw new IllegalArgumentException("Unknown LogLevel: " + loglevel.toString()); + } + } + + // TODO: javadoc + /** + * + * @param log + * @param loglevel + * @param msg + * @since 7.0.1 + */ + private static final void logMessageAtLevel( Logger log, LogLevel loglevel, String msg ) { + switch( loglevel ) { + case TRACE: + log.trace(msg); + return; + case DEBUG: + log.debug(msg); + return; + case INFO: + log.info(msg); + return; + case WARN: + log.warn(msg); + return; + case ERROR: + log.error(msg); + return; + } + } + + + + // ========== LOCAL CLASSES ========== + + private class SortableCtxListElement implements Comparable { + HashMap child_elements = new HashMap(); + String[] sort_fields; + + public SortableCtxListElement( SvcLogicContext ctx, String root, String[] sort_fields ) { + this.sort_fields = sort_fields; + + for( String key : ctx.getAttributeKeySet() ) { + if( key.startsWith(root) ) { + if( key.length() == root.length() ) { + child_elements.put("", ctx.getAttribute(key)); + break; + } + else { + child_elements.put(key.substring(root.length()+1), ctx.getAttribute(key)); + } + } + } + } + + @Override + public int compareTo(SortableCtxListElement arg0) { + if( sort_fields == null ) { + return this.child_elements.get("").compareTo(arg0.child_elements.get("")); + } + + for( String field : this.sort_fields ) { + int result = this.child_elements.get(field).compareTo(arg0.child_elements.get(field)); + if( result != 0 ) { + return result; + } + } + + return 0; + } + } + + /** + * Creates a file that contains the content of context memory. + * @param parameters - must contain the parameter filename * @param ctx Reference to context memory - * @param pfx Prefix of key-value pairs to remove - * @since 1.0 - */ - public static final void ctxBulkErase( SvcLogicContext ctx, String pfx ) { - ArrayList Keys = new ArrayList<>( ctx.getAttributeKeySet() ); - for( String key : Keys ) { - if( key.startsWith( pfx ) ) { - ctx.setAttribute( pfx + key.substring(pfx.length()) , null); - } - } - } - - /** - * Copies all context memory key-value pairs that start with src_pfx to - * the keys that start with dest_pfx + suffix, where suffix is the result - * of {@code key.substring(src_pfx.length())}. - *

- * Does NOT guarantee removal of all keys at the destination before - * copying, but will overwrite any destination keys that have a - * corresponding source key. Use {@link #ctxBulkErase(SvcLogicContext, String) ctxBulkErase} - * before copy to erase destination root before copying from source. - * @param ctx Reference to context memory. - * @param src_pfx Prefix of the keys to copy values from. - * @param dest_pfx Prefix of the keys to copy values to. - * @since 1.0 - */ - public static final void ctxBulkCopy( SvcLogicContext ctx, String src_pfx, String dest_pfx ) { - // Remove trailing period from dest_pfx - if( dest_pfx.charAt(dest_pfx.length()-1) == '.' ) { - dest_pfx = dest_pfx.substring(0,dest_pfx.length()-1); - } - - // For each context key that begins with src_pfx, set the value of the - // key dest_pfx + the suffix of the key to the key's value - ArrayList Keys = new ArrayList<>(ctx.getAttributeKeySet()); - for( String key : Keys ) { - if( key.startsWith(src_pfx) ) { - // Get suffix (no leading period) - String suffix = key.substring(src_pfx.length()); - if( suffix.charAt(0) == '.') { - suffix = suffix.substring(1); - } - - // Set destination's value to key's value - ctx.setAttribute(dest_pfx + '.' + suffix, ctx.getAttribute(key)); - } - } - } - - /** - * Creates and returns a {@code Map} that is a subset of - * context memory where all keys begin with the prefix. - * @param ctx Reference to context memory. - * @param prefix Returned map's keys should all begin with this value. - * @return A {@code Map} containing all the key-value pairs - * in ctx whose key begins with prefix. - */ - public static final Map ctxGetBeginsWith( SvcLogicContext ctx, String prefix ) { - Map prefixMap = new HashMap<>(); - - for( String key : ctx.getAttributeKeySet() ) { - if( key.startsWith(prefix) ) { - prefixMap.put( key, ctx.getAttribute(key) ); - } - } - - return prefixMap; - } - - /** - * Returns true if key's value in context memory is "" or if it doesn't - * exist in context memory. - * @param ctx Reference to context memory. - * @param key Key to search for. - * @return true if key's value in context memory is "" or if it doesn't - * exist in context memory. - * @since 1.0 - */ - public static final boolean ctxKeyEmpty( SvcLogicContext ctx, String key ) { - String value = ctx.getAttribute(key); - return value == null || value.isEmpty(); - } - - /** - * Adds all key-value pairs in the entries Map to context memory. - * @param ctx Reference to context memory. Value's {@code toString()} - * function is used to add it. - * @param entries {@code Map} of key-value pairs to add to - * context memory. Value's {@code toString()} function is used to add it. - * @return Reference to context memory to be used for function chaining. + * @throws SvcLogicException thrown if file cannot be created or if parameters are missing */ - public static final SvcLogicContext ctxPutAll( SvcLogicContext ctx, Map entries ) { - for( Map.Entry entry : entries.entrySet() ) { - ctxSetAttribute( ctx, entry.getKey(), entry.getValue() ); - //ctx.setAttribute(entry.getKey(), entry.getValue().toString()); - } - - return ctx; - } - - /** - * Sets a key in context memory to the output of object's toString(). The - * key is deleted from context memory if object is null. - * @param ctx Reference to context memory. - * @param key Key to set. - * @param object Object whose toString() will be the value set - */ - public static final void ctxSetAttribute( SvcLogicContext ctx, String key, Object object ) { - if( object == null ) { - ctx.setAttribute(key, null); - } - else { - ctx.setAttribute(key, object.toString()); - } - } - - /** - * Sets a key in context memory to the output of object's toString(). - *

- * The key is deleted from context memory if object is null. The key and - * value set in context memory are logged to the Logger at the provided - * logLevel level. - * @param Any Java object - * @param ctx Reference to context memory. - * @param key Key to set. - * @param obj Object whose toString() will be the value set - * @param LOG Logger to log to - * @param logLevel level to log at in Logger + public static void printContext(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + if (parameters == null || parameters.keySet().size() < 1) { + throw new SvcLogicException("no parameters passed"); + } + + checkParameters(parameters, new String[]{"filename"}, LOG); + + String fileName = parameters.get("filename"); + + PrintStream pstr = null; + + try { + pstr = new PrintStream(new FileOutputStream(new File(fileName), true)); + } catch (Exception e) { + throw new SvcLogicException("Cannot open file " + fileName, e); + } + pstr.println("#######################################"); + for (String attr : ctx.getAttributeKeySet()) { + pstr.println(attr + " = " + ctx.getAttribute(attr)); + } + pstr.flush(); + pstr.close(); + } + + /** + * Checks context memory for a set of required parameters + * Every parameter aside from prefix will be treated as mandatory + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + *
parameterMandatory/Optionaldescription
prefixOptionalthe prefix will be added to each parameter
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 */ - public static final void ctxSetAttribute( SvcLogicContext ctx, String key, O obj, Logger LOG, LogLevel logLevel ) { - String value = Objects.toString( obj, null ); - ctx.setAttribute( key, value ); - if( logLevelIsEnabled(LOG, logLevel ) ) { - if( value == null ) { - logMessageAtLevel( LOG, logLevel, "Deleting " + key ); + public static void requiredParameters(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + if (parameters == null || parameters.keySet().size() < 1) { + String errorMessage = "requiredParameters should not be called if the parameters hashmap is null or empty!"; + LOG.error(errorMessage); + throw new SvcLogicException(errorMessage); + } + String prefixValue = null; + String prefix = "prefix"; + if(parameters.containsKey(prefix)){ + prefixValue = parameters.get(prefix); + parameters.remove(prefix); + } + checkParameters(prefixValue, ctx.getAttributeKeySet(), parameters.keySet(), LOG); + } + + private static void checkParameters(String prefixValue, Set ctx, Set parameters, Logger log) throws SvcLogicException { + for (String param : parameters) { + if (prefixValue != null) { + param = prefixValue + param; } - else { - logMessageAtLevel( LOG, logLevel, "Setting " + key + " = " + value ); + if (!ctx.contains(param)) { + String errorMessage = "This method requires the parameters [" + StringUtils.join(parameters, ",") + + "], but " + param + " was not passed in."; + log.error(errorMessage); + throw new SvcLogicException(errorMessage); } } } /** - * Utility function used to get an array's length from context memory. - * Will return 0 if key doesn't exist in context memory or isn't numeric. - *

- * Use to obtain a context memory array length without having to worry - * about throwing a NumberFormatException. - * @param ctx Reference to context memory - * @param key Key in context memory whose value is the array's length. If - * the key doesn't end in "_length", then "_length is appended. - * @param log Reference to Logger to log to - * @return The array length or 0 if the key is not found in context memory. - * @since 1.0 + * is in a different DG invocation just before/after we call NCS and set the state to InProgress */ - public static final int getArrayLength( SvcLogicContext ctx, String key ) { - return getArrayLength(ctx, key, null, null, null); - } - /** - * Utility function used to get an array's length from context memory. - * Will return 0 if key doesn't exist in context memory or isn't numeric - * and print the provided log message to the configured log file. - *

- * Use to obtain a context memory array length without having to worry - * about throwing a NumberFormatException. - * @param ctx Reference to context memory. - * @param key Key in context memory whose value is the array's length. If - * the key doesn't end in "_length", then "_length is appended. - * @param log Reference to Logger to log to. Doesn't log if null. - * @param logLevel Logging level to log the message at if the context - * memory key isn't found. Doesn't log if null. - * @param log_message Message to log if the context memory key isn't found. - * Doesn't log if null. - * @return The array length or 0 if the key is not found in context memory. - * @since 1.0 - */ - public static final int getArrayLength( SvcLogicContext ctx, String key, Logger log, LogLevel logLevel, String log_message ) { - String ctxKey = key.endsWith("_length") ? key : key + "_length"; + * setTime write the current date time to a string located at outputPath + * @param parameters - requires outputPath to not be null + * @param ctx Reference to context memory + * @throws SvcLogicException if a required parameter is missing an exception is thrown + */ + public static void setTime(Map parameters, SvcLogicContext ctx) throws SvcLogicException + { + checkParameters(parameters, new String[] { "outputPath" }, LOG); + + // Set the DateFormat + // "2015-03-16T12:18:35.138Z" + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + + // Parse the date + String ctxVariable = parameters.get("outputPath"); try { - return Integer.parseInt(ctx.getAttribute(ctxKey)); - } - catch( NumberFormatException e ) { - if( log != null && logLevel != null && log_message != null ) { - switch( logLevel ) { - case TRACE: - log.trace(log_message); - case DEBUG: - log.debug(log_message); - break; - case INFO: - log.info(log_message); - break; - case WARN: - log.warn(log_message); - break; - case ERROR: - log.error(log_message); - break; - } - } - } - - return 0; - } - - /** - * Prints sorted context memory key-value pairs to the log file at the log - * level. Returns immediately if the log level isn't enabled. - *

- * O(n log(n)) time where n = size of context memory - * @param ctx Reference to context memory - * @param log Reference to Logger to log to - * @param logLevel Logging level to log the context memory key-value pairs - * at. - * @since 1.0 - */ - public static final void logContextMemory( SvcLogicContext ctx, Logger log, LogLevel logLevel ) { - logLevelIsEnabled( log, logLevel ); - - // Print sorted context memory key-value pairs to the log - ArrayList keys = new ArrayList<>(ctx.getAttributeKeySet()); - Collections.sort(keys); - for( String key : keys ) { - logMessageAtLevel( log, logLevel, key + " = " + ctx.getAttribute(key) ); - } - } - - - - // ========== PRIVATE FUNCTIONS ========== - - // TODO: javadoc - /** - * - * @param parameters - * @param log - * @param loglevel - * @since 7.0.1 - */ - public static final void logExecuteNodeParameters( Map parameters, Logger log, LogLevel loglevel ) { - logLevelIsEnabled( log, loglevel ); - - for( Map.Entry param : parameters.entrySet() ) { - logMessageAtLevel( log, loglevel, "PARAM: " + param.getKey() + " = " + param.getValue() ); - } - } - - // TODO: javadoc - /** - * Returns true if the loglevel is enabled. Otherwise, returns false. - * @param log Reference to logger - * @param loglevel Log level to check if enabled - * @return True if the loglevel is enabled. Otherwise, false - * @since 7.0.1 - */ - private static final boolean logLevelIsEnabled( Logger log, LogLevel loglevel ) { - // Return immediately if logging level isn't enabled - switch( loglevel ) { - case TRACE: - if( log.isTraceEnabled() ) { return true; } - return false; - case DEBUG: - if( log.isDebugEnabled() ) { return true; } - return false; - case INFO: - if( log.isInfoEnabled() ) { return true; } - return false; - case WARN: - if( log.isWarnEnabled() ) { return true; } - return false; - case ERROR: - if( log.isErrorEnabled() ) { return true; } - return false; - default: - throw new IllegalArgumentException("Unknown LogLevel: " + loglevel.toString()); - } - } - - // TODO: javadoc - /** - * - * @param log - * @param loglevel - * @param msg - * @since 7.0.1 - */ - private static final void logMessageAtLevel( Logger log, LogLevel loglevel, String msg ) { - switch( loglevel ) { - case TRACE: - log.trace(msg); - return; - case DEBUG: - log.debug(msg); - return; - case INFO: - log.info(msg); - return; - case WARN: - log.warn(msg); - return; - case ERROR: - log.error(msg); - return; - } - } - - - - // ========== LOCAL CLASSES ========== - - private class SortableCtxListElement implements Comparable { - HashMap child_elements = new HashMap<>(); - String[] sort_fields; - - public SortableCtxListElement( SvcLogicContext ctx, String root, String[] sort_fields ) { - this.sort_fields = sort_fields; - - for( String key : ctx.getAttributeKeySet() ) { - if( key.startsWith(root) ) { - if( key.length() == root.length() ) { - child_elements.put("", ctx.getAttribute(key)); - break; - } - else { - child_elements.put(key.substring(root.length()+1), ctx.getAttribute(key)); - } - } - } - } - - @Override - public int compareTo(SortableCtxListElement arg0) { - if( sort_fields == null ) { - return this.child_elements.get("").compareTo(arg0.child_elements.get("")); - } - - for( String field : this.sort_fields ) { - int result = this.child_elements.get(field).compareTo(arg0.child_elements.get(field)); - if( result != 0 ) { - return result; - } - } - - return 0; + String dateTime = format.format(new Date()); + ctx.setAttribute(ctxVariable, dateTime); + } catch (Exception ex) { + throw new SvcLogicException("problem with setTime", ex); } } } diff --git a/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtilsActivator.java b/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtilsActivator.java index 27addba..e0568ab 100644 --- a/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtilsActivator.java +++ b/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtilsActivator.java @@ -21,7 +21,10 @@ package org.openecomp.sdnc.sli.SliPluginUtils; - +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.Properties; @@ -32,55 +35,59 @@ import org.osgi.framework.ServiceRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - public class SliPluginUtilsActivator implements BundleActivator { - -// private static final String SLIPLUGINUTILS_PROP_VAR = "/slipluginutils.properties"; -// private static final String SDNC_CONFIG_DIR = "SDNC_CONFIG_DIR"; - - @SuppressWarnings("rawtypes") - private final List registrations = new LinkedList<>(); + @SuppressWarnings("rawtypes") private List registrations = new LinkedList(); private static final Logger LOG = LoggerFactory.getLogger(SliPluginUtilsActivator.class); + private static final String SDNC_ROOT_DIR = "SDNC_CONFIG_DIR"; + private static final String DME2_PROPERTIES_FILE_NAME = "dme2.properties"; @Override public void start(BundleContext ctx) throws Exception { - // Read properties - Properties props = new Properties(); - - // ---uncomment below when adding properties file--- - /* - String propDir = System.getenv(SDNC_CONFIG_DIR); - if (propDir == null) { - throw new ConfigurationException( - "Cannot find config file - " + SLIPLUGINUTILS_PROP_VAR + " and " + SDNC_CONFIG_DIR + " unset"); - } - String propPath = propDir + SLIPLUGINUTILS_PROP_VAR; - - File propFile = new File(propPath); + SliPluginUtils plugin = new SliPluginUtils(new Properties()); + LOG.info("Registering service " + plugin.getClass().getName()); + registrations.add(ctx.registerService(plugin.getClass().getName(), plugin, null)); - if (!propFile.exists()) { - throw new ConfigurationException("Missing configuration properties file : " + propFile); - } + SliStringUtils sliStringUtils_Plugin = new SliStringUtils(); + LOG.info("Registering service " + sliStringUtils_Plugin.getClass().getName()); + registrations.add(ctx.registerService(sliStringUtils_Plugin.getClass().getName(), sliStringUtils_Plugin, null)); try { - props.load(new FileInputStream(propFile)); + String path = System.getenv(SDNC_ROOT_DIR) + File.separator + DME2_PROPERTIES_FILE_NAME; + DME2 dmePlugin = initDme2(path); + if (dmePlugin != null) { + LOG.info("Registering service " + dmePlugin.getClass().getName()); + registrations.add(ctx.registerService(dmePlugin.getClass().getName(), dmePlugin, null)); + } } catch (Exception e) { - throw new ConfigurationException("Could not load properties file " + propPath, e); + LOG.error("DME2 plugin could not be started", e); } - */ + } - SliPluginUtils plugin = new SliPluginUtils(props); + public DME2 initDme2(String pathToDmeProperties) { + Properties dme2properties = new Properties(); + String loadPropertiesErrorMessage = "Couldn't load DME2 properties at path " + pathToDmeProperties; + File dme2propertiesFile = new File(pathToDmeProperties); - LOG.info("Registering service "+plugin.getClass().getName()); - registrations.add(ctx.registerService(plugin.getClass().getName(), plugin, null)); + try { + dme2properties.load(new FileReader(dme2propertiesFile)); + String proxyUrlProperty = dme2properties.getProperty("proxyUrl"); + String[] proxyUrls = proxyUrlProperty.split(","); + DME2 dmePlugin = new DME2(dme2properties.getProperty("aafUserName"), dme2properties.getProperty("aafPassword"), dme2properties.getProperty("envContext"), dme2properties.getProperty("routeOffer"), proxyUrls, dme2properties.getProperty("commonServiceVersion")); + dmePlugin.setPartner(dme2properties.getProperty("partner")); + return dmePlugin; + } catch (FileNotFoundException e) { + LOG.error(loadPropertiesErrorMessage); + } catch (IOException e) { + LOG.error(loadPropertiesErrorMessage); + } + LOG.error("Couldn't create DME2 plugin"); + return null; } @Override public void stop(BundleContext ctx) throws Exception { - - for (@SuppressWarnings("rawtypes") ServiceRegistration registration: registrations) - { + for (@SuppressWarnings("rawtypes") ServiceRegistration registration : registrations) { registration.unregister(); registration = null; } diff --git a/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliStringUtils.java b/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliStringUtils.java new file mode 100644 index 0000000..43af6f5 --- /dev/null +++ b/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SliStringUtils.java @@ -0,0 +1,396 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.SliPluginUtils; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicException; +import org.openecomp.sdnc.sli.SvcLogicJavaPlugin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A SvcLogicJavaPlugin that exposes java.lang.String functions to DirectedGraph + */ +public class SliStringUtils implements SvcLogicJavaPlugin { + private static final Logger LOG = LoggerFactory.getLogger(SliStringUtils.class); + + public SliStringUtils() {} + + /** + * Provides split functionality to Directed Graphs. + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
original_stringMandatoryString to perform split on
regexMandatorythe delimiting regular expression
limitOptionalresult threshold. See String.split method for further description. Defaults to 0
ctx_memory_result_keyMandatoryKey in context memory to populate the resulting array of strings under
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + * @see String#split(String, int) + */ + public void split( Map parameters, SvcLogicContext ctx ) throws SvcLogicException { + final String original_string = parameters.get("original_string"); + LOG.trace("original_string = " + original_string); + final String regex = parameters.get("regex"); + LOG.trace("regex = " + regex); + final String limit_str = parameters.get("limit"); + LOG.trace("limit_str = " + limit_str); + final String ctx_memory_result_key = parameters.get("ctx_memory_result_key"); + LOG.trace("ctx_memory_result_key = " + ctx_memory_result_key); + + try { + // Validation that parameters are not null + SliPluginUtils.checkParameters( parameters, new String[]{"original_string","regex","ctx_memory_result_key"}, LOG ); + + // Read limit from context memory. Default to 0 if null/empty + int limit = 0; + if( StringUtils.isNotEmpty(limit_str) ) { + try { + limit = Integer.parseInt(limit_str); + } + catch( NumberFormatException e ) { + throw new IllegalArgumentException( "The limit parameter of the SliStringUtils.split() function must be a number, empty string, or null", e ); + } + } + + // Call String.split(regex,limit) on string passed in + String[] split_string = original_string.split(regex, limit); + + // Populate context memory with results + for( int i = 0; i < split_string.length; i++ ) { + SliPluginUtils.ctxSetAttribute(ctx, ctx_memory_result_key + '[' + i + ']', split_string[i], LOG, SliPluginUtils.LogLevel.DEBUG); + } + SliPluginUtils.ctxSetAttribute(ctx, ctx_memory_result_key + "_length", new Integer(split_string.length), LOG, SliPluginUtils.LogLevel.DEBUG); + } + catch( Exception e ) { + // Have error message print parameters + throw new SvcLogicException( "An error occurred during SliStringUtils.split() where original_string = " + quotedOrNULL(regex) + + " regex = " + quotedOrNULL(regex) + + " limit = " + quotedOrNULL(regex) + + " ctx_memory_result_key = " + quotedOrNULL(regex), e ); + } + } + + private static String quotedOrNULL( String str ) { + return (str == null) ? "NULL" : '"' + str + '"'; + } + + /** + * exposes equalsIgnoreCase to directed graph + * @param parameters HashMap of parameters passed by the DG to this function + * emits a true or false outcome + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
sourceMandatorysource string
targetMandatorytarget string
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static String equalsIgnoreCase(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + SliPluginUtils.checkParameters(parameters, new String[]{"source","target"}, LOG); + if(parameters.get("source").equalsIgnoreCase(parameters.get("target"))){ + return "true"; + } + return "false"; + } + + /** + * exposes toUpperCase to directed graph + * writes an upperCase version of source to outputPath + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
sourceMandatorysource string
outputPathMandatorythe location in context memory the result is written to
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void toUpper(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + SliPluginUtils.checkParameters(parameters, new String[]{"source","outputPath"}, LOG); + ctx.setAttribute(parameters.get("outputPath"), parameters.get("source").toUpperCase()); + } + + /** + * exposes toLowerCase to directed graph + * writes a lowerCase version of source to outputPath + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
sourceMandatorysource string
outputPathMandatorythe location in context memory the result is written to
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void toLower(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + SliPluginUtils.checkParameters(parameters, new String[]{"source","outputPath"}, LOG); + ctx.setAttribute(parameters.get("outputPath"), parameters.get("source").toLowerCase()); + } + + /** + * exposes contains to directed graph to test if one string contains another + * tests if the source contains the target + * @param parameters HashMap of parameters passed by the DG to this function + * emits a true or false outcome + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
sourceMandatorysource string
targetMandatorytarget string
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static String contains(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + SliPluginUtils.checkParameters(parameters, new String[]{"source","target"}, LOG); + if(parameters.get("source").contains(parameters.get("target"))){ + return "true"; + } + return "false"; + } + + /** + * exposes endsWith to directed graph to test if one string endsWith another string + * tests if the source ends with the target + * @param parameters HashMap of parameters passed by the DG to this function + * emits a true or false outcome + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
sourceMandatorysource string
targetMandatorytarget string
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static String endsWith(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + SliPluginUtils.checkParameters(parameters, new String[]{"source","target"}, LOG); + if(parameters.get("source").endsWith(parameters.get("target"))){ + return "true"; + } + return "false"; + } + + /** + * exposes startsWith to directed graph to test if one string endsWith another string + * tests if the source ends with the target + * @param parameters HashMap of parameters passed by the DG to this function + * emits a true or false outcome + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
sourceMandatorysource string
targetMandatorytarget string
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static String startsWith(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + SliPluginUtils.checkParameters(parameters, new String[]{"source","target"}, LOG); + if(parameters.get("source").startsWith(parameters.get("target"))){ + return "true"; + } + return "false"; + } + + /** + * exposes trim to directed graph + * writes a trimmed version of the string to the outputPath + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
sourceMandatorysource string
outputPathMandatorythe location in context memory the result is written to
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void trim(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + SliPluginUtils.checkParameters(parameters, new String[]{"source","outputPath"}, LOG); + ctx.setAttribute(parameters.get("outputPath"), parameters.get("source").trim()); + } + + /** + * exposes String.length() to directed graph + * writes the length of source to outputPath + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
sourceMandatorysource string
outputPathMandatorythe location in context memory the result is written to
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void getLength(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + SliPluginUtils.checkParameters(parameters, new String[]{"source","outputPath"}, LOG); + ctx.setAttribute(parameters.get("outputPath"), String.valueOf(parameters.get("source").length())); + } + + /** + * exposes replace to directed graph + * writes the length of source to outputPath + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
sourceMandatorysource string
targetMandatoryThe sequence of char values to be replaced
replacementMandatoryThe replacement sequence of char values
outputPathMandatorythe location in context memory the result is written to
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void replace(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + SliPluginUtils.checkParameters(parameters, new String[]{"source","outputPath","target","replacement"}, LOG); + ctx.setAttribute(parameters.get("outputPath"), (parameters.get("source").replace(parameters.get("target"), parameters.get("replacement")))); + } + + /** + * Provides substring functionality to Directed Graphs. + *

+ * Calls either String.substring(String beginIndex) or + * String.substring(String beginInded, String endIndex) if the end-index + * is present or not. + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
stringMandatoryString to perform substring on
resultMandatoryKey in context memory to populate the resulting string in
begin-indexMandatoryBeginning index to pass to Java substring function
end-indexOptionalEnding index to pass to Java substring function. If not included, String.substring(begin) will be called.
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public void substring( Map parameters, SvcLogicContext ctx ) throws SvcLogicException { + try { + SliPluginUtils.checkParameters( parameters, new String[]{"string","begin-index","result"}, LOG ); + final String string = parameters.get("string"); + final String result = parameters.get("result"); + final String begin = parameters.get("begin-index"); + final String end = parameters.get("end-index"); + if( StringUtils.isEmpty(end) ) { + ctx.setAttribute( result, string.substring(Integer.parseInt(begin)) ); + } + else { + ctx.setAttribute( result, string.substring(Integer.parseInt(begin), Integer.parseInt(end)) ); + } + } + catch( Exception e ) { + throw new SvcLogicException( "An error occurred while the Directed Graph was performing a substring", e ); + } + } + + /** + * Provides concat functionality to Directed Graphs. + *

+ * Will concat target to source and write the result to outputPath + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
sourceMandatorysource string
targetMandatoryThe sequence of char values to be replaced
outputPathMandatorythe location in context memory the result is written to
+ * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void concat( Map parameters, SvcLogicContext ctx ) throws SvcLogicException { + SliPluginUtils.checkParameters( parameters, new String[]{"source","target","outputPath"}, LOG ); + String result = parameters.get("source").concat(parameters.get("target")); + ctx.setAttribute(parameters.get("outputPath"), result); + } + + /** + * Provides url encoding functionality to Directed Graphs. + *

+ * Will url encode the source and write the result to outputPath + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
sourceMandatorysource string
encodingOptionalthe name of a supported character encoding, defaulted to UTF-8 if not supplied
outputPathMandatorythe location in context memory the result is written to
+ * @param ctx Reference to context memory + * @throws SvcLogicException + */ + public static void urlEncode(Map parameters, SvcLogicContext ctx) throws SvcLogicException { + SliPluginUtils.checkParameters(parameters, new String[] { "source", "outputPath" }, LOG); + String encoding = parameters.get("encoding"); + if (encoding == null) { + encoding = "UTF-8"; + } + try { + String result = URLEncoder.encode(parameters.get("source"), encoding); + ctx.setAttribute(parameters.get("outputPath"), result); + } catch (UnsupportedEncodingException e) { + throw new SvcLogicException("Url encode failed.", e); + } + } + +} diff --git a/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SvcLogicContextList.java b/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SvcLogicContextList.java index 22f07f9..89a4a98 100644 --- a/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SvcLogicContextList.java +++ b/sliPluginUtils/provider/src/main/java/org/openecomp/sdnc/sli/SliPluginUtils/SvcLogicContextList.java @@ -60,14 +60,14 @@ public class SvcLogicContextList { // Initialize list int capacity = getCtxListLength(ctx, prefix); - this.list = new ArrayList<>( capacity ); + this.list = new ArrayList>(capacity); for( int i = 0; i < capacity; i++ ) { this.list.add(i, new HashMap()); } // Populate "elements" in list String prefix_bracket = this.prefix + '['; - for( String key : new HashSet<>(ctx.getAttributeKeySet()) ) { + for (String key : new HashSet(ctx.getAttributeKeySet())) { if( key.startsWith(prefix_bracket) ) { // Extract the index of the list int index = getCtxListIndex(key, this.prefix, capacity); @@ -160,8 +160,7 @@ public class SvcLogicContextList { for( Map.Entry entry : this.list.get(i).entrySet() ) { if( entry.getKey().equals("") ) { ctx.setAttribute(prefix + '[' + i + ']', entry.getValue()); - } - else { + } else { ctx.setAttribute(prefix + '[' + i + "]." + entry.getKey(), entry.getValue()); } } @@ -176,16 +175,9 @@ public class SvcLogicContextList { private static int getCtxListIndex( String key, String prefix, int list_size ) { int index = getCtxListIndex( key, prefix ); if( index >= list_size ) { - throw new IllegalArgumentException( - "Context memory list \"" + prefix + "[]\" contains an index >= the size of the list", - new ArrayIndexOutOfBoundsException( "index \"" + index + "\" is outside the bounds of the context memory list \"" + prefix + "[]. List Length = " + list_size ) - ); - } - else if( index < 0 ) { - throw new IllegalArgumentException( - "Context memory list \"" + prefix + "[]\" contains a negative index", - new NegativeArraySizeException( "index \"" + index + "\" of context memory list is negative" ) - ); + throw new IllegalArgumentException("Context memory list \"" + prefix + "[]\" contains an index >= the size of the list", new ArrayIndexOutOfBoundsException("index \"" + index + "\" is outside the bounds of the context memory list \"" + prefix + "[]. List Length = " + list_size)); + } else if (index < 0) { + throw new IllegalArgumentException("Context memory list \"" + prefix + "[]\" contains a negative index", new NegativeArraySizeException("index \"" + index + "\" of context memory list is negative")); } return index; @@ -196,8 +188,7 @@ public class SvcLogicContextList { String ctx_index_str = StringUtils.substringBetween(key.substring(prefix.length()), "[", "]"); try { return Integer.parseInt( ctx_index_str ); - } - catch( NumberFormatException e ) { + } catch (NumberFormatException e) { throw new IllegalStateException("Could not parse index value \"" + ctx_index_str + "\" in context memory key \"" + key + "\"", e); } } @@ -208,12 +199,10 @@ public class SvcLogicContextList { String _length_val_str = ctx.getAttribute(_length_key); try { return Integer.parseInt(_length_val_str); - } - catch( NumberFormatException e ) { + } catch (NumberFormatException e) { if( _length_val_str == null ) { throw new IllegalStateException( "Could not find list length \"" + _length_key + "\" in context memory." ); - } - else { + } else { throw new IllegalStateException( "Could not parse index value \"" + _length_val_str + "\" of context memory list length \"" + _length_key + "\"" , e ); } } diff --git a/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/CheckParametersTest.java b/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/CheckParametersTest.java new file mode 100644 index 0000000..318e464 --- /dev/null +++ b/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/CheckParametersTest.java @@ -0,0 +1,116 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.SliPluginUtils; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CheckParametersTest { + + @Test + public void nullRequiredParameters() throws Exception { + Map parametersMap = new HashMap(); + String[] requiredParams = null; + Logger Log = LoggerFactory.getLogger(SliPluginUtils.class); + SliPluginUtils.checkParameters(parametersMap, requiredParams, Log); + } + + @Test(expected = SvcLogicException.class) + public void emptyParametersMap() throws Exception { + Map parametersMap = new HashMap(); + String[] requiredParams = new String[] { "param1", "param2", "param3" }; + Logger Log = LoggerFactory.getLogger(SliPluginUtils.class); + SliPluginUtils.checkParameters(parametersMap, requiredParams, Log); + } + + @Test(expected = SvcLogicException.class) + public void paramNotFound() throws Exception { + Map parametersMap = new HashMap(); + parametersMap.put("tst", "me"); + String[] requiredParams = new String[] { "param1", "parm2", "param3" }; + Logger Log = LoggerFactory.getLogger(SliPluginUtils.class); + SliPluginUtils.checkParameters(parametersMap, requiredParams, Log); + } + + @Test + public void testSunnyRequiredParameters() throws Exception { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("param1", "hello"); + ctx.setAttribute("param2", "world"); + ctx.setAttribute("param3", "!"); + + Map parameters = new HashMap(); + parameters.put("param1", "dog"); + parameters.put("param2", "cat"); + parameters.put("param3", "fish"); + + SliPluginUtils.requiredParameters(parameters, ctx); + } + + @Test + public void testSunnyRequiredParametersWithPrefix() throws Exception { + String prefixValue = "my.unique.path."; + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute(prefixValue + "param1", "hello"); + ctx.setAttribute(prefixValue + "param2", "world"); + ctx.setAttribute(prefixValue + "param3", "!"); + + Map parameters = new HashMap(); + parameters.put("prefix", prefixValue); + parameters.put("param1", "dog"); + parameters.put("param2", "cat"); + parameters.put("param3", "fish"); + + SliPluginUtils.requiredParameters(parameters, ctx); + } + + @Test(expected = SvcLogicException.class) + public void testRainyMissingRequiredParameters() throws Exception { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("param1", "hello"); + ctx.setAttribute("param3", "!"); + + Map parameters = new HashMap(); + parameters.put("param1", null); + parameters.put("param2", null); + parameters.put("param3", null); + + SliPluginUtils.requiredParameters(parameters, ctx); + } + + @Test(expected = SvcLogicException.class) + public void testEmptyRequiredParameters() throws Exception { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("param1", "hello"); + ctx.setAttribute("param3", "!"); + + Map parameters = new HashMap(); + + SliPluginUtils.requiredParameters(parameters, ctx); + } +} diff --git a/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/Dme2Test.java b/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/Dme2Test.java new file mode 100644 index 0000000..d66b011 --- /dev/null +++ b/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/Dme2Test.java @@ -0,0 +1,109 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.SliPluginUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; +import org.openecomp.sdnc.sli.SliPluginUtils.DME2; +import org.openecomp.sdnc.sli.SliPluginUtils.SliPluginUtilsActivator; + +public class Dme2Test { + + @Test + public void createInstarUrl() { + String instarUrl = "http://localhost:25055/service=sample.com/services/eim/v1/rest/version=1702.0/envContext=TEST/routeOffer=DEFAULT/subContext=/enterpriseConnection/getEnterpriseConnectionDetails/v1?dme2.password=fake&dme2.username=user@sample.com"; + DME2 dme = new DME2("user@sample.com", "fake", "TEST", "DEFAULT", new String[] { "http://localhost:25055" }, "common"); + String constructedUrl = dme.constructUrl("sample.com/services/eim/v1/rest", "1702.0", "/enterpriseConnection/getEnterpriseConnectionDetails/v1"); + assertEquals(instarUrl, constructedUrl); + } + + @Test + public void createInstarUrlNoSubContext() { + String instarUrl = "http://localhost:25055/service=sample.com/services/eim/v1/rest/version=1702.0/envContext=TEST/routeOffer=DEFAULT?dme2.password=fake&dme2.username=user@sample.com"; + DME2 dme = new DME2("user@sample.com", "fake", "TEST", "DEFAULT", new String[] { "http://localhost:25055" }, "common"); + Map parameters = new HashMap(); + String constructedUrl = dme.constructUrl("sample.com/services/eim/v1/rest", "1702.0", parameters.get(null)); + assertEquals(instarUrl, constructedUrl); + } + + @Test + public void testRoundRobin() { + String[] proxyHostNames = new String[] { "http://one:25055", "http://two:25055", "http://three:25055" }; + String urlSuffix = "/service=sample.com/services/eim/v1/rest/version=1702.0/envContext=TEST/routeOffer=DEFAULT/subContext=/enterpriseConnection/getEnterpriseConnectionDetails/v1?dme2.password=fake&dme2.username=user@sample.com"; + DME2 dme = new DME2("user@sample.com", "fake", "TEST", "DEFAULT", proxyHostNames, "common"); + String constructedUrl = dme.constructUrl("sample.com/services/eim/v1/rest", "1702.0", "/enterpriseConnection/getEnterpriseConnectionDetails/v1"); + assertEquals(proxyHostNames[0] + urlSuffix, constructedUrl); + constructedUrl = dme.constructUrl("sample.com/services/eim/v1/rest", "1702.0", "/enterpriseConnection/getEnterpriseConnectionDetails/v1"); + assertEquals(proxyHostNames[1] + urlSuffix, constructedUrl); + constructedUrl = dme.constructUrl("sample.com/services/eim/v1/rest", "1702.0", "/enterpriseConnection/getEnterpriseConnectionDetails/v1"); + assertEquals(proxyHostNames[2] + urlSuffix, constructedUrl); + constructedUrl = dme.constructUrl("sample.com/services/eim/v1/rest", "1702.0", "/enterpriseConnection/getEnterpriseConnectionDetails/v1"); + assertEquals(proxyHostNames[0] + urlSuffix, constructedUrl); + constructedUrl = dme.constructUrl("sample.com/services/eim/v1/rest", "1702.0", "/enterpriseConnection/getEnterpriseConnectionDetails/v1"); + assertEquals(proxyHostNames[1] + urlSuffix, constructedUrl); + constructedUrl = dme.constructUrl("sample.com/services/eim/v1/rest", "1702.0", "/enterpriseConnection/getEnterpriseConnectionDetails/v1"); + assertEquals(proxyHostNames[2] + urlSuffix, constructedUrl); + constructedUrl = dme.constructUrl("sample.com/services/eim/v1/rest", "1702.0", "/enterpriseConnection/getEnterpriseConnectionDetails/v1"); + assertEquals(proxyHostNames[0] + urlSuffix, constructedUrl); + } + + @Test + public void createDme2EndtoEnd() { + SliPluginUtilsActivator activator = new SliPluginUtilsActivator(); + DME2 dme2 = activator.initDme2("src/test/resources/dme2.e2e.properties"); + assertEquals("user@sample.com", dme2.aafUserName); + assertEquals("fake", dme2.aafPassword); + assertEquals("UAT", dme2.envContext); + assertEquals("UAT", dme2.routeOffer); + Assert.assertArrayEquals("http://sample.com:25055,http://sample.com:25055".split(","), dme2.proxyUrls); + assertEquals("1702.0", dme2.commonServiceVersion); + assertEquals(null, dme2.partner); + + String constructedUrl = dme2.constructUrl("sample.com/restservices/instar/v1/assetSearch", null, "/mySubContext"); + assertNotNull(constructedUrl); + System.out.println(constructedUrl); + } + + @Test + public void createDme2Prod() { + SliPluginUtilsActivator activator = new SliPluginUtilsActivator(); + DME2 dme2 = activator.initDme2("src/test/resources/dme2.prod.properties"); + assertEquals("user@sample.com", dme2.aafUserName); + assertEquals("fake", dme2.aafPassword); + assertEquals("PROD", dme2.envContext); + assertEquals("", dme2.routeOffer); + Assert.assertArrayEquals("http://sample.com:25055,http://sample.com:25055".split(","), dme2.proxyUrls); + assertEquals("1.0", dme2.commonServiceVersion); + assertEquals("LPP_PROD", dme2.partner); + + String constructedUrl = dme2.constructUrl("sample.com/restservices/instar/v1/assetSearch", null, "/mySubContext"); + assertNotNull(constructedUrl); + System.out.println(constructedUrl); + } + +} diff --git a/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils_StaticFunctionsTest.java b/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils_StaticFunctionsTest.java new file mode 100644 index 0000000..e69d086 --- /dev/null +++ b/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils_StaticFunctionsTest.java @@ -0,0 +1,250 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.SliPluginUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SliPluginUtils_StaticFunctionsTest { + private static final Logger LOG = LoggerFactory.getLogger(SliPluginUtils_StaticFunctionsTest.class); + SliPluginUtils utils = new SliPluginUtils(); + private SvcLogicContext ctx; + private HashMap parameters; + + @Before + public void setUp() throws Exception { + this.ctx = new SvcLogicContext(); + parameters = new HashMap(); + } + + // TODO: javadoc + @Test + public final void testCtxGetBeginsWith() { + ctx.setAttribute("service-data.oper-status.order-status", "InProgress"); + ctx.setAttribute("service-data.service-information.service-instance-id", "my-instance"); + ctx.setAttribute("service-data.service-information.service-type", "my-service"); + + Map entries = SliPluginUtils.ctxGetBeginsWith(ctx, "service-data.service-information"); + + assertEquals("my-instance", entries.get("service-data.service-information.service-instance-id")); + assertEquals("my-service", entries.get("service-data.service-information.service-type")); + assertFalse(entries.containsKey("service-data.oper-status.order-status")); + } + + // TODO: javadoc + @Test + public final void testCtxListRemove_index() throws SvcLogicException { + LOG.trace("=== testCtxListRemove_index ==="); + ctx.setAttribute("service-data.vnf-l3[0].vnf-host-name", "vnf-host-name_0"); + ctx.setAttribute("service-data.vnf-l3[0].device-host-name", "device-host-name_0"); + ctx.setAttribute("service-data.vnf-l3[1].vnf-host-name", "vnf-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[1].device-host-name", "device-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[2].vnf-host-name", "vnf-host-name_2"); + ctx.setAttribute("service-data.vnf-l3[2].device-host-name", "device-host-name_2"); + ctx.setAttribute("service-data.vnf-l3_length", "3"); + + parameters.put("index", "1"); + parameters.put("list_pfx", "service-data.vnf-l3"); + + utils.ctxListRemove(parameters, ctx); + SliPluginUtils.logContextMemory(ctx, LOG, SliPluginUtils.LogLevel.TRACE); + + assertEquals("2", ctx.getAttribute("service-data.vnf-l3_length")); + assertEquals("vnf-host-name_0", ctx.getAttribute("service-data.vnf-l3[0].vnf-host-name")); + assertEquals("device-host-name_0", ctx.getAttribute("service-data.vnf-l3[0].device-host-name")); + assertEquals("vnf-host-name_2", ctx.getAttribute("service-data.vnf-l3[1].vnf-host-name")); + assertEquals("device-host-name_2", ctx.getAttribute("service-data.vnf-l3[1].device-host-name")); + } + + // TODO: javadoc + @Test + public final void textCtxListRemove_keyValue() throws SvcLogicException { + LOG.trace("=== textCtxListRemove_keyValue ==="); + ctx.setAttribute("service-data.vnf-l3[0].vnf-host-name", "vnf-host-name_0"); + ctx.setAttribute("service-data.vnf-l3[0].device-host-name", "device-host-name_0"); + ctx.setAttribute("service-data.vnf-l3[1].vnf-host-name", "vnf-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[1].device-host-name", "device-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[2].vnf-host-name", "vnf-host-name_2"); + ctx.setAttribute("service-data.vnf-l3[2].device-host-name", "device-host-name_2"); + // 2nd entry + ctx.setAttribute("service-data.vnf-l3[3].vnf-host-name", "vnf-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[3].device-host-name", "device-host-name_1"); + ctx.setAttribute("service-data.vnf-l3_length", "4"); + + parameters.put("list_pfx", "service-data.vnf-l3"); + parameters.put("key", "vnf-host-name"); + parameters.put("value", "vnf-host-name_1"); + + utils.ctxListRemove(parameters, ctx); + SliPluginUtils.logContextMemory(ctx, LOG, SliPluginUtils.LogLevel.TRACE); + + assertEquals("2", ctx.getAttribute("service-data.vnf-l3_length")); + assertEquals("vnf-host-name_0", ctx.getAttribute("service-data.vnf-l3[0].vnf-host-name")); + assertEquals("device-host-name_0", ctx.getAttribute("service-data.vnf-l3[0].device-host-name")); + assertEquals("vnf-host-name_2", ctx.getAttribute("service-data.vnf-l3[1].vnf-host-name")); + assertEquals("device-host-name_2", ctx.getAttribute("service-data.vnf-l3[1].device-host-name")); + } + + // TODO: javadoc + @Test + public final void textCtxListRemove_keyValue_nullkey() throws SvcLogicException { + LOG.trace("=== textCtxListRemove_keyValue_nullkey ==="); + ctx.setAttribute("service-data.vnf-l3[0]", "vnf-host-name_0"); + ctx.setAttribute("service-data.vnf-l3[1]", "vnf-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[2]", "vnf-host-name_2"); + ctx.setAttribute("service-data.vnf-l3_length", "3"); + + parameters.put("list_pfx", "service-data.vnf-l3"); + parameters.put("value", "vnf-host-name_1"); + + utils.ctxListRemove(parameters, ctx); + SliPluginUtils.logContextMemory(ctx, LOG, SliPluginUtils.LogLevel.TRACE); + + assertEquals("2", ctx.getAttribute("service-data.vnf-l3_length")); + assertEquals("vnf-host-name_0", ctx.getAttribute("service-data.vnf-l3[0]")); + assertEquals("vnf-host-name_2", ctx.getAttribute("service-data.vnf-l3[1]")); + } + + // TODO: javadoc + @Test + public final void textCtxListRemove_keyValueList() throws SvcLogicException { + LOG.trace("=== textCtxListRemove_keyValueList ==="); + ctx.setAttribute("service-data.vnf-l3[0].vnf-host-name", "vnf-host-name_0"); + ctx.setAttribute("service-data.vnf-l3[0].device-host-name", "device-host-name_0"); + ctx.setAttribute("service-data.vnf-l3[1].vnf-host-name", "vnf-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[1].device-host-name", "device-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[2].vnf-host-name", "vnf-host-name_2"); + ctx.setAttribute("service-data.vnf-l3[2].device-host-name", "device-host-name_2"); + // 2nd entry + ctx.setAttribute("service-data.vnf-l3[3].vnf-host-name", "vnf-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[3].device-host-name", "device-host-name_1"); + // entries with only 1 of 2 key-value pairs matching + ctx.setAttribute("service-data.vnf-l3[4].vnf-host-name", "vnf-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[4].device-host-name", "device-host-name_4"); + ctx.setAttribute("service-data.vnf-l3[5].vnf-host-name", "vnf-host-name_5"); + ctx.setAttribute("service-data.vnf-l3[5].device-host-name", "device-host-name_1"); + ctx.setAttribute("service-data.vnf-l3_length", "6"); + + parameters.put("list_pfx", "service-data.vnf-l3"); + parameters.put("keys_length", "2"); + parameters.put("keys[0].key", "vnf-host-name"); + parameters.put("keys[0].value", "vnf-host-name_1"); + parameters.put("keys[1].key", "device-host-name"); + parameters.put("keys[1].value", "device-host-name_1"); + + utils.ctxListRemove(parameters, ctx); + SliPluginUtils.logContextMemory(ctx, LOG, SliPluginUtils.LogLevel.TRACE); + + assertEquals("4", ctx.getAttribute("service-data.vnf-l3_length")); + assertEquals("vnf-host-name_0", ctx.getAttribute("service-data.vnf-l3[0].vnf-host-name")); + assertEquals("device-host-name_0", ctx.getAttribute("service-data.vnf-l3[0].device-host-name")); + assertEquals("vnf-host-name_2", ctx.getAttribute("service-data.vnf-l3[1].vnf-host-name")); + assertEquals("device-host-name_2", ctx.getAttribute("service-data.vnf-l3[1].device-host-name")); + assertEquals("vnf-host-name_1", ctx.getAttribute("service-data.vnf-l3[2].vnf-host-name")); + assertEquals("device-host-name_4", ctx.getAttribute("service-data.vnf-l3[2].device-host-name")); + assertEquals("vnf-host-name_5", ctx.getAttribute("service-data.vnf-l3[3].vnf-host-name")); + assertEquals("device-host-name_1", ctx.getAttribute("service-data.vnf-l3[3].device-host-name")); + } + + // TODO: javadoc + @Test(expected = SvcLogicException.class) + public final void testCtxListRemove_nullListLength() throws SvcLogicException { + LOG.trace("=== testCtxListRemove_nullListLength ==="); + ctx.setAttribute("service-data.vnf-l3[0].vnf-host-name", "vnf-host-name_0"); + ctx.setAttribute("service-data.vnf-l3[0].device-host-name", "device-host-name_0"); + ctx.setAttribute("service-data.vnf-l3[1].vnf-host-name", "vnf-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[1].device-host-name", "device-host-name_1"); + ctx.setAttribute("service-data.vnf-l3[2].vnf-host-name", "vnf-host-name_2"); + ctx.setAttribute("service-data.vnf-l3[2].device-host-name", "device-host-name_2"); + + parameters.put("index", "1"); + parameters.put("list_pfx", "service-data.vnf-l3"); + + utils.ctxListRemove(parameters, ctx); + } + + // TODO: javadoc + @Test + public final void testCtxPutAll() { + HashMap entries = new HashMap(); + entries.put("service-data.oper-status.order-status", "InProgress"); + entries.put("service-data.service-information.service-instance-id", "my-instance"); + entries.put("service-data.request-information.order-number", 1234); + entries.put("service-data.request-information.request-id", null); + + SliPluginUtils.ctxPutAll(ctx, entries); + + assertEquals("InProgress", ctx.getAttribute("service-data.oper-status.order-status")); + assertEquals("my-instance", ctx.getAttribute("service-data.service-information.service-instance-id")); + assertEquals("1234", ctx.getAttribute("service-data.request-information.order-number")); + assertFalse(ctx.getAttributeKeySet().contains("service-data.request-information.request-id")); + } + + // TODO: javadoc + @Test + public final void testCtxSetAttribute_LOG() { + LOG.debug("=== testCtxSetAttribute_LOG ==="); + Integer i = new Integer(3); + SliPluginUtils.ctxSetAttribute(ctx, "test", i, LOG, SliPluginUtils.LogLevel.TRACE); + } + + /*@Test + public void printContext() throws SvcLogicException, IOException { + String filePath = "/src/test/resources/printContext.txt"; + parameters.put("filename", filePath); + File f = new File(filePath); + assert (f.exists()); + assert (!f.isDirectory()); + ctx.setAttribute("hello", "world"); + ctx.setAttribute("name", "value"); + + SliPluginUtils.printContext(parameters, ctx); + BufferedReader br = new BufferedReader(new FileReader(f)); + String line = br.readLine(); + assertEquals("#######################################", line); + line = br.readLine(); + assertEquals("hello = world", line); + line = br.readLine(); + assertEquals("name = value", line); + br.close(); + Files.delete(Paths.get(filePath)); + }*/ + + @Test + public void setTime() throws SvcLogicException { + String outputPath = "output"; + parameters.put("outputPath", outputPath); + SliPluginUtils.setTime(parameters, ctx); + assertNotNull(ctx.getAttribute(outputPath)); + } +} diff --git a/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils_ctxSortListTest.java b/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils_ctxSortListTest.java new file mode 100644 index 0000000..e2fcb33 --- /dev/null +++ b/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliPluginUtils_ctxSortListTest.java @@ -0,0 +1,97 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdnc.sli.SliPluginUtils; + +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.Random; + +import org.junit.Before; +import org.junit.Test; +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("unused") +public class SliPluginUtils_ctxSortListTest { + private static final Logger LOG = LoggerFactory.getLogger(SliPluginUtils_ctxSortListTest.class); + SliPluginUtils utils = new SliPluginUtils(); + SvcLogicContext ctx; + HashMap parameters; + Random rand = new Random(); + + @Before + public void setUp() throws Exception { + this.ctx = new SvcLogicContext(); + this.parameters = new HashMap(); + } + + @Test + public final void list_of_containers() throws SvcLogicException { + this.parameters.put("list", "input.list"); + this.parameters.put("sort-fields", "sort-key"); + this.parameters.put("delimiter", ","); + + ctx.setAttribute("input.list_length", "10"); + for (int i = 0; i < 10; i++) { + this.ctx.setAttribute("input.list[" + i + "].sort-key", Integer.toString(rand.nextInt(10))); + this.ctx.setAttribute("input.list[" + i + "].value", Integer.toString(rand.nextInt(10))); + } + + LOG.trace("BEFORE SORT:"); + SliPluginUtils.logContextMemory(ctx, LOG, SliPluginUtils.LogLevel.TRACE); + + utils.ctxSortList(this.parameters, this.ctx); + + LOG.trace("AFTER SORT:"); + SliPluginUtils.logContextMemory(ctx, LOG, SliPluginUtils.LogLevel.TRACE); + + for (int i = 0; i < 9; i++) { + assertTrue(this.ctx.getAttribute("input.list[" + i + "].sort-key").compareTo(this.ctx.getAttribute("input.list[" + (i + 1) + "].sort-key")) < 1); + } + } + + @Test + public final void list_of_elements() throws SvcLogicException { + this.parameters.put("list", "input.list"); + this.parameters.put("delimiter", ","); + + this.ctx.setAttribute("input.list_length", "10"); + for (int i = 0; i < 10; i++) { + this.ctx.setAttribute("input.list[" + i + ']', Integer.toString(rand.nextInt(10))); + } + + LOG.trace("BEFORE SORT:"); + SliPluginUtils.logContextMemory(ctx, LOG, SliPluginUtils.LogLevel.TRACE); + + utils.ctxSortList(this.parameters, this.ctx); + + LOG.trace("AFTER SORT:"); + SliPluginUtils.logContextMemory(ctx, LOG, SliPluginUtils.LogLevel.TRACE); + + for (int i = 0; i < 9; i++) { + assertTrue(this.ctx.getAttribute("input.list[" + i + ']').compareTo(this.ctx.getAttribute("input.list[" + (i + 1) + ']')) < 1); + } + } +} diff --git a/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliStringUtilsTest.java b/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliStringUtilsTest.java new file mode 100644 index 0000000..d57cefa --- /dev/null +++ b/sliPluginUtils/provider/src/test/java/org/openecomp/sdnc/sli/SliPluginUtils/SliStringUtilsTest.java @@ -0,0 +1,244 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/** + * + */ +package org.openecomp.sdnc.sli.SliPluginUtils; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.openecomp.sdnc.sli.SvcLogicContext; +import org.openecomp.sdnc.sli.SvcLogicException; + +/** + * @author km991u + * + */ +public class SliStringUtilsTest { + private SvcLogicContext ctx; + private HashMap param; + private SliStringUtils stringUtils = new SliStringUtils(); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + this.ctx = new SvcLogicContext(); + param = new HashMap(); + } + + /** + * @throws SvcLogicException + * @see SliStringUtils#split(Map, SvcLogicContext) + */ + @Test + public final void testSplit() throws SvcLogicException { + param.put("original_string", "one ## two ## three"); + param.put("regex", " ## "); + param.put("ctx_memory_result_key", "result"); + + stringUtils.split(param, ctx); + + assertThat(ctx.getAttribute("result[0]"), equalTo("one")); + assertThat(ctx.getAttribute("result[1]"), equalTo("two")); + assertThat(ctx.getAttribute("result[2]"), equalTo("three")); + assertThat(ctx.getAttribute("result_length"), equalTo("3")); + } + + /** + * @throws SvcLogicException + * @see SliStringUtils#split(Map, SvcLogicContext) + */ + @Test + public final void testSplit_limit() throws SvcLogicException { + param.put("original_string", "one ## two ## three"); + param.put("regex", " ## "); + param.put("limit", "2"); + param.put("ctx_memory_result_key", "result"); + + stringUtils.split(param, ctx); + + assertThat(ctx.getAttribute("result[0]"), equalTo("one")); + assertThat(ctx.getAttribute("result[1]"), equalTo("two ## three")); + assertThat(ctx.getAttribute("result_length"), equalTo("2")); + } + + @Test + public void equalsIgnoreCaseTrue() throws SvcLogicException { + String sourceString = "HeLlOwORLD"; + String targetSTring = "HELLOWORLD"; + param.put("source", sourceString); + param.put("target", targetSTring); + assertEquals("true", SliStringUtils.equalsIgnoreCase(param, ctx)); + } + + @Test + public void equalsIgnoreCaseFalse() throws SvcLogicException { + String sourceString = "HeLlOwORLD"; + String targetSTring = "goodbyeWORLD"; + param.put("source", sourceString); + param.put("target", targetSTring); + assertEquals("false", SliStringUtils.equalsIgnoreCase(param, ctx)); + } + + @Test + public void toUpper() throws SvcLogicException { + String sourceString = "HeLlOwORLD"; + param.put("source", sourceString); + String path = "my.unique.path."; + param.put("outputPath", path); + SliStringUtils.toUpper(param, ctx); + assertEquals(sourceString.toUpperCase(), ctx.getAttribute(path)); + } + + @Test + public void toLower() throws SvcLogicException { + String sourceString = "HeLlOwORLD"; + param.put("source", sourceString); + String path = "my.unique.path."; + param.put("outputPath", path); + SliStringUtils.toLower(param, ctx); + assertEquals(sourceString.toLowerCase(), ctx.getAttribute(path)); + } + + @Test + public void containsTrue() throws SvcLogicException { + String sourceString = "Pizza"; + String targetSTring = "izza"; + param.put("source", sourceString); + param.put("target", targetSTring); + assertEquals("true", SliStringUtils.contains(param, ctx)); + } + + @Test + public void containsFalse() throws SvcLogicException { + String sourceString = "Pizza"; + String targetSTring = "muffin"; + param.put("source", sourceString); + param.put("target", targetSTring); + assertEquals("false", SliStringUtils.contains(param, ctx)); + } + + @Test + public void endsWithTrue() throws SvcLogicException { + String sourceString = "Pizza"; + String targetSTring = "za"; + param.put("source", sourceString); + param.put("target", targetSTring); + assertEquals("true", SliStringUtils.endsWith(param, ctx)); + } + + @Test + public void endsWithFalse() throws SvcLogicException { + String sourceString = "Pizza"; + String targetSTring = "muffin"; + param.put("source", sourceString); + param.put("target", targetSTring); + assertEquals("false", SliStringUtils.endsWith(param, ctx)); + } + + @Test + public void trim() throws SvcLogicException { + String sourceString = " H E L L O W O R L D"; + String outputPath = "muffin"; + param.put("source", sourceString); + param.put("outputPath", outputPath); + SliStringUtils.trim(param, ctx); + assertEquals(sourceString.trim(), ctx.getAttribute(outputPath)); + } + + @Test + public void getLength() throws SvcLogicException { + String sourceString = "SomeRandomString"; + String outputPath = "muffin"; + param.put("source", sourceString); + param.put("outputPath", outputPath); + SliStringUtils.getLength(param, ctx); + assertEquals(String.valueOf(sourceString.length()), ctx.getAttribute(outputPath)); + } + + @Test + public void startsWithFalse() throws SvcLogicException { + String sourceString = "Java"; + String targetSTring = "DG"; + param.put("source", sourceString); + param.put("target", targetSTring); + assertEquals("false", SliStringUtils.startsWith(param, ctx)); + } + + @Test + public void startsWithTrue() throws SvcLogicException { + String sourceString = "Java"; + String targetSTring = "Ja"; + param.put("source", sourceString); + param.put("target", targetSTring); + assertEquals("true", SliStringUtils.startsWith(param, ctx)); + } + + @Test + public void replace() throws SvcLogicException { + String sourceString = "cat Hello World cat"; + String old = "cat"; + String neww = "dog"; + String outputPath = "out"; + + param.put("source", sourceString); + param.put("target", old); + param.put("replacement", neww); + param.put("outputPath", outputPath); + SliStringUtils.replace(param, ctx); + assertEquals(sourceString.replace(old, neww), ctx.getAttribute(outputPath)); + } + + @Test + public void concat() throws SvcLogicException { + String sourceString = "cat"; + String targetString = "dog"; + String outputPath = "out"; + + param.put("source", sourceString); + param.put("target", targetString); + param.put("outputPath", outputPath); + SliStringUtils.concat(param, ctx); + assertEquals(sourceString + targetString, ctx.getAttribute(outputPath)); + } + + @Test + public void urlEncode() throws SvcLogicException { + String sourceString = "102/GE100/SNJSCAMCJP8/SNJSCAMCJT4"; + String outputPath = "out"; + + param.put("source", sourceString); + param.put("outputPath", outputPath); + SliStringUtils.urlEncode(param, ctx); + assertEquals("102%2FGE100%2FSNJSCAMCJP8%2FSNJSCAMCJT4", ctx.getAttribute(outputPath)); + } + +} diff --git a/sliapi/installer/pom.xml b/sliapi/installer/pom.xml index 52237dc..ec54c3e 100755 --- a/sliapi/installer/pom.xml +++ b/sliapi/installer/pom.xml @@ -8,7 +8,6 @@ 1.1.0-SNAPSHOT sliapi-installer - SLI API - Karaf Installer pom diff --git a/sliapi/model/pom.xml b/sliapi/model/pom.xml index ae79cb3..7d7e4c9 100755 --- a/sliapi/model/pom.xml +++ b/sliapi/model/pom.xml @@ -30,7 +30,7 @@ org.opendaylight.mdsal maven-sal-api-gen-plugin - ${odl.yangtools.version} + ${odl.sal.api.gen.plugin.version} jar @@ -58,7 +58,7 @@ org.opendaylight.mdsal yang-binding - ${odl.yangtools.version} + ${odl.mdsal.yang.binding.version} org.opendaylight.yangtools diff --git a/sliapi/model/src/main/yang/sliapi.yang b/sliapi/model/src/main/yang/sliapi.yang index 2c77331..047fd69 100755 --- a/sliapi/model/src/main/yang/sliapi.yang +++ b/sliapi/model/src/main/yang/sliapi.yang @@ -48,7 +48,10 @@ module SLI-API { leaf ack-final-indicator { type string; } - leaf response-text { + leaf response-message { + type string; + } + leaf context-memory-json { type string; } } diff --git a/sliapi/provider/pom.xml b/sliapi/provider/pom.xml index 96e81b4..62d9cb0 100755 --- a/sliapi/provider/pom.xml +++ b/sliapi/provider/pom.xml @@ -54,7 +54,7 @@ org.opendaylight.mdsal maven-sal-api-gen-plugin - ${odl.yangtools.version} + ${odl.sal.api.gen.plugin.version} jar diff --git a/sliapi/provider/src/main/java/org/openecomp/sdnc/sliapi/sliapiProvider.java b/sliapi/provider/src/main/java/org/openecomp/sdnc/sliapi/sliapiProvider.java index 8189bc0..f8deef2 100644 --- a/sliapi/provider/src/main/java/org/openecomp/sdnc/sliapi/sliapiProvider.java +++ b/sliapi/provider/src/main/java/org/openecomp/sdnc/sliapi/sliapiProvider.java @@ -203,7 +203,7 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ if (svcLogic == null) { respBuilder.setResponseCode("500"); - respBuilder.setResponseText("Could not locate OSGi SvcLogicService service"); + respBuilder.setResponseMessage("Could not locate OSGi SvcLogicService service"); respBuilder.setAckFinalIndicator("Y"); rpcResult = RpcResultBuilder. status(true).withResult(respBuilder.build()).build(); @@ -214,7 +214,7 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ try { if (!svcLogic.hasGraph(calledModule, calledRpc, null, modeStr)) { respBuilder.setResponseCode("404"); - respBuilder.setResponseText("Directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr+" not found"); + respBuilder.setResponseMessage("Directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr+" not found"); respBuilder.setAckFinalIndicator("Y"); rpcResult = RpcResultBuilder. status(true).withResult(respBuilder.build()).build(); @@ -224,7 +224,7 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ LOG.error("Caught exception looking for directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr, e); respBuilder.setResponseCode("500"); - respBuilder.setResponseText("Internal error : could not determine if target graph exists"); + respBuilder.setResponseMessage("Internal error : could not determine if target graph exists"); respBuilder.setAckFinalIndicator("Y"); rpcResult = RpcResultBuilder. status(true).withResult(respBuilder.build()).build(); @@ -265,7 +265,7 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ try { LOG.info("Calling directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr); - if (LOG.isDebugEnabled()) { + if (LOG.isTraceEnabled()) { StringBuffer argList = new StringBuffer(); argList.append("Parameters : {"); Enumeration e = parms.propertyNames(); @@ -274,7 +274,7 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ argList.append(" ("+propName+","+parms.getProperty(propName)+") "); } argList.append("}"); - LOG.debug(argList.toString()); + LOG.trace(argList.toString()); argList = null; } @@ -283,9 +283,22 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ Properties respProps = svcLogic.execute(calledModule, calledRpc, null, modeStr, parms, domDataBroker); + StringBuilder sb = new StringBuilder("{"); + + for (Object key : respProps.keySet()) { + String keyValue = (String) key; + if (keyValue != null && !"".equals(keyValue) && !keyValue.contains("input.sli-parameter")) { + sb.append("\"").append(keyValue).append("\": \"").append(respProps.getProperty(keyValue)).append("\","); + } + } + + sb.setLength(sb.length() - 1); + sb.append("}"); + respBuilder.setResponseCode(respProps.getProperty("error-code", "0")); - respBuilder.setResponseText(respProps.getProperty("error-message", "")); + respBuilder.setResponseMessage(respProps.getProperty("error-message", ""));// TODO change response-text to response-message to match other BVC APIs respBuilder.setAckFinalIndicator(respProps.getProperty("ack-final", "Y")); + respBuilder.setContextMemoryJson(sb.toString()); TestResultBuilder testResultBuilder = new TestResultBuilder(); @@ -308,7 +321,7 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ respBuilder.setResponseCode("500"); respBuilder - .setResponseText("Internal error : caught exception executing directed graph " + .setResponseMessage("Internal error : caught exception executing directed graph " + calledModule + "/" + calledRpc @@ -359,7 +372,7 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ if (svcLogic == null) { respBuilder.setResponseCode("500"); - respBuilder.setResponseText("Could not locate OSGi SvcLogicService service"); + respBuilder.setResponseMessage("Could not locate OSGi SvcLogicService service"); respBuilder.setAckFinalIndicator("Y"); rpcResult = RpcResultBuilder. failed().withResult(respBuilder.build()).build(); @@ -369,7 +382,7 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ try { if (!svcLogic.hasGraph(calledModule, calledRpc, null, modeStr)) { respBuilder.setResponseCode("404"); - respBuilder.setResponseText("Directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr+" not found"); + respBuilder.setResponseMessage("Directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr+" not found"); respBuilder.setAckFinalIndicator("Y"); @@ -380,7 +393,7 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ LOG.error("Caught exception looking for directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr, e); respBuilder.setResponseCode("500"); - respBuilder.setResponseText("Internal error : could not determine if target graph exists"); + respBuilder.setResponseMessage("Internal error : could not determine if target graph exists"); respBuilder.setAckFinalIndicator("Y"); rpcResult = RpcResultBuilder. failed().withResult(respBuilder.build()).build(); @@ -396,7 +409,7 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ null, modeStr, parms); respBuilder.setResponseCode(respProps.getProperty("error-code", "0")); - respBuilder.setResponseText(respProps.getProperty("error-message", "")); + respBuilder.setResponseMessage(respProps.getProperty("error-message", "")); respBuilder.setAckFinalIndicator(respProps.getProperty("ack-final", "Y")); } catch (Exception e) { @@ -405,7 +418,7 @@ public class sliapiProvider implements AutoCloseable, SLIAPIService{ respBuilder.setResponseCode("500"); respBuilder - .setResponseText("Internal error : caught exception executing directed graph " + .setResponseMessage("Internal error : caught exception executing directed graph " + calledModule + "/" + calledRpc -- 2.16.6