diff options
111 files changed, 19969 insertions, 2414 deletions
@@ -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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!DOCTYPE document [ + <!ENTITY project SYSTEM "@TOMCAT_PROJECT_DEST@"> +]> +<?xml-stylesheet type="text/xsl" href="package.xsl"?> +<document url="changelog.html"> + + &project; + + <properties> + <author email="fhanik@apache.org">Filip Hanik</author> + <title>Changelog</title> + </properties> + +<body> + +<section name="Tomcat JDBC Connection Pool - Apache Tomcat 7.0.19 and later"> + <p> + Starting with Apache Tomcat 7.0.19 in July 2011, Tomcat JDBC Connection Pool + is built and released as a component in official releases of Tomcat. + The changes are now listed in "jdbc-pool" sections of Apache Tomcat + changelog file. This changelog file is obsolete. + </p> +</section> + +<section name="Tomcat JDBC Connection Pool 1.1.0.0"> + <subsection name="pool"> + <changelog> + <add><rev>1207712</rev> Pool cleaner should be a global thread, not spawn one thread per connection pool. (fhanik)</add> + <fix><rev>1073531</rev> <bug>50805</bug> Only initialize connections once when async (fhanik)</fix> + <fix><rev>1076380</rev> <bug>50857</bug> Correctly handle timeouts when the pool is busy when async (fhanik)</fix> + <add>Added QueryTimeoutInterceptor to be able to configure timeouts on running queries automatically.</add> + </changelog> + </subsection> +</section> + +<section name="Tomcat JDBC Connection Pool 1.0.9.4"> + <subsection name="pool"> + <changelog> + <fix><rev>1069864</rev> <bug>50759</bug> Correctly set validation timestamp when using external validator.(fhanik)</fix> + </changelog> + </subsection> +</section> + +<section name="Tomcat JDBC Connection Pool 1.0.9.3"> + <subsection name="pool"> + <changelog> + <fix><rev>1060998</rev> <bug>50613</bug> Fix concurrency issue around pool size calculation.(fhanik)</fix> + </changelog> + </subsection> +</section> +<section name="Tomcat JDBC Connection Pool 1.0.9.2"> + <subsection name="pool"> + <changelog> + <fix><rev>1057743</rev> Make sure passwords are masked.(fhanik)</fix> + </changelog> + </subsection> +</section> +<section name="Tomcat JDBC Connection Pool 1.0.9.0"> + <subsection name="pool"> + <changelog> + <fix><rev>997321</rev> Ensure threads borrowing connections do not + get stuck waiting for a new connection if a connection is released in + another thread. (markt)</fix> + <fix><rev>995432</rev> Make interceptor class names, property names + and property values tolerant of whitespace by trimming the values before + use. (markt)</fix> + <fix><rev>995091</rev> <bug>49831</bug> Make sure pooled XAConnections are + closed when the connection pool shuts down. Patch provided by Daniel + Mikusa. (markt)</fix> + <update><rev>995087</rev> Code clean-up. Remove some unused code. (markt) + </update> + <update><rev>995083</rev> Update to Tomcat 6.0.29 (for JULI). (markt) + </update> + <update><rev>992409</rev> Code clean-up. Reduce sequences of three or more + blank lines to two blank lines. (markt)</update> + <add><rev>952811</rev>, <rev>995095</rev> <bug>48814</bug> Add Validator + interface and allow users to configure a Validator class name. Patch + provided by Matt Passell. (markt)</add> + <update><rev>948073</rev> Code clean-up. Remove unused imports. (markt) + </update> + <fix><rev>943434</rev> <bug>49224</bug> Only try setting the username and + password if they are non-null. Patch provided by Matt Passell. (markt) + </fix> + <fix><rev>943032</rev> <bug>49269</bug> Set maxIdle to maxActive by + default to prevent warning on start when maxIdle > maxActive. Patch + provided by Matt Passell. (markt)</fix> + <fix><rev>940574</rev> <bug>49241</bug> Don't ignore the + suspectTimeout property. (fhanik)</fix> + <fix><rev>939320</rev> Fix svn:keywords for property replacement. + (kkolinko)</fix> + <add><rev>931550</rev>, <rev>934651</rev>, <rev>934677</rev> Add a + statement cache. (fhanik)</add> + <update><rev>919076</rev> Improve XA support. (fhanik)</update> + <fix><rev>915940</rev> <bug>48392</bug> Add an interceptor to wrap + Statements and ResultSets to prevent access to the physical connection. + (fhanik)</fix> + <fix><rev>912026</rev> Call <code>setTransactionIsolation()</code> before + anything else as some drivers require this to be the first call. (fhanik) + </fix> + <update><rev>900017</rev> Update Javadoc for XADataSource. (kkolinko) + </update> + </changelog> + </subsection> +</section> +<section name="Tomcat JDBC Connection Pool prior to 1.0.9.0 (incomplete)"> + <subsection name="pool"> + <changelog> + <update><rev>720253</rev> Document how to use interceptors</update> + <update><rev>717972</rev> Added an interceptor that will clean up non closed statements when a connection is returned to the pool. (<code>org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer</code>)</update> + <update><rev>713763</rev> Improve connection state handling</update> + <fix><rev>713763</rev> Improve connection state handling</fix> + </changelog> + </subsection> +</section> +</body> +</document> 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!DOCTYPE document [ + <!ENTITY project SYSTEM "@TOMCAT_PROJECT_DEST@"> +]> +<document url="jdbc-pool.html"> + + &project; + + <properties> + <author email="fhanik@apache.org">Filip Hanik</author> + <title>The Tomcat JDBC Connection Pool</title> + </properties> + +<body> + +<section name="Table of Contents"> +<toc/> +</section> + +<section name="Introduction"> + + <p>The <strong>JDBC Connection Pool <code>org.apache.tomcat.jdbc.pool</code></strong> + is a replacement or an alternative to the <a href="http://commons.apache.org/dbcp/">Apache Commons DBCP</a> + connection pool.</p> + + <p>So why do we need a new connection pool?</p> + + <p>Here are a few of the reasons:</p> + <ol> + <li>Commons DBCP 1.x is single threaded. In order to be thread safe + Commons locks the entire pool for short periods during both object + allocation and object return. Note that this does not apply to + Commons DBCP 2.x.</li> + <li>Commons DBCP 1.x can be slow. As the number of logical CPUs grows and + the number of concurrent threads attempting to borrow or return + objects increases, the performance suffers. For highly concurrent + systems the impact can be significant. Note that this does not apply + to Commons DBCP 2.x.</li> + <li>Commons DBCP is over 60 classes. tomcat-jdbc-pool core is 8 classes, + hence modifications for future requirement will require much less + changes. This is all you need to run the connection pool itself, the + rest is gravy.</li> + <li>Commons DBCP uses static interfaces. This means you have to use the + right version for a given JRE version or you may see + <code>NoSuchMethodException</code> exceptions.</li> + <li>It's not worth rewriting over 60 classes, when a connection pool can + be accomplished with a much simpler implementation.</li> + <li>Tomcat jdbc pool implements the ability retrieve a connection + asynchronously, without adding additional threads to the library + itself.</li> + <li>Tomcat jdbc pool is a Tomcat module, it depends on Tomcat JULI, a + simplified logging framework used in Tomcat.</li> + <li>Retrieve the underlying connection using the + <code>javax.sql.PooledConnection</code> interface.</li> + <li>Starvation proof. If a pool is empty, and threads are waiting for a + connection, when a connection is returned, the pool will awake the + correct thread waiting. Most pools will simply starve.</li> + </ol> + + <p>Features added over other connection pool implementations</p> + <ol> + <li>Support for highly concurrent environments and multi core/cpu systems.</li> + <li>Dynamic implementation of interface, will support <code>java.sql</code> and <code>javax.sql</code> interfaces for + your runtime environment (as long as your JDBC driver does the same), even when compiled with a lower version of the JDK.</li> + <li>Validation intervals - we don't have to validate every single time we use the connection, we can do this + when we borrow or return the connection, just not more frequent than an interval we can configure.</li> + <li>Run-Once query, a configurable query that will be run only once, when the connection to the database is established. + Very useful to setup session settings, that you want to exist during the entire time the connection is established.</li> + <li>Ability to configure custom interceptors. + This allows you to write custom interceptors to enhance the functionality. You can use interceptors to gather query stats, + cache session states, reconnect the connection upon failures, retry queries, cache query results, and so on. + Your options are endless and the interceptors are dynamic, not tied to a JDK version of a + <code>java.sql</code>/<code>javax.sql</code> interface.</li> + <li>High performance - we will show some differences in performance later on</li> + <li>Extremely simple, due to the very simplified implementation, the line count and source file count are very low, compare with c3p0 + that has over 200 source files(last time we checked), Tomcat jdbc has a core of 8 files, the connection pool itself is about half + that. As bugs may occur, they will be faster to track down, and easier to fix. Complexity reduction has been a focus from inception.</li> + <li>Asynchronous connection retrieval - you can queue your request for a connection and receive a <code>Future<Connection></code> back.</li> + <li>Better idle connection handling. Instead of closing connections directly, it can still pool connections and sizes the idle pool with a smarter algorithm.</li> + <li>You can decide at what moment connections are considered abandoned, is it when the pool is full, or directly at a timeout + by specifying a pool usage threshold. + </li> + <li>The abandon connection timer will reset upon a statement/query activity. Allowing a connections that is in use for a long time to not timeout. + This is achieved using the <code>ResetAbandonedTimer</code> + </li> + <li>Close connections after they have been connected for a certain time. Age based close upon return to the pool. + </li> + <li>Get JMX notifications and log entries when connections are suspected for being abandoned. This is similar to + the <code>removeAbandonedTimeout</code> but it doesn't take any action, only reports the information. + This is achieved using the <code>suspectTimeout</code> attribute.</li> + <li>Connections can be retrieved from a <code>java.sql.Driver</code>, <code>javax.sql.DataSource</code> or <code>javax.sql.XADataSource</code> + This is achieved using the <code>dataSource</code> and <code>dataSourceJNDI</code> attributes.</li> + <li>XA connection support</li> + </ol> + + +</section> +<section name="How to use"> + <p> + Usage of the Tomcat connection pool has been made to be as simple as possible, for those of you that are familiar with commons-dbcp, the + transition will be very simple. Moving from other connection pools is also fairly straight forward. + </p> + <subsection name="Additional features"> + <p>The Tomcat connection pool offers a few additional features over what most other pools let you do:</p> + <ul> + <li><code>initSQL</code> - the ability to run a SQL statement exactly once, when the connection is created</li> + <li><code>validationInterval</code> - in addition to running validations on connections, avoid running them too frequently.</li> + <li><code>jdbcInterceptors</code> - flexible and pluggable interceptors to create any customizations around the pool, + the query execution and the result set handling. More on this in the advanced section.</li> + <li><code>fairQueue</code> - Set the fair flag to true to achieve thread fairness or to use asynchronous connection retrieval</li> + </ul> + </subsection> + <subsection name="Inside the Apache Tomcat Container"> + <p> + The Tomcat Connection pool is configured as a resource described in <a href="http://tomcat.apache.org/tomcat-8.0-doc/jndi-datasource-examples-howto.html" target="_blank">The Tomcat JDBC documentation</a> + With the only difference being that you have to specify the <code>factory</code> attribute and set the value to + <code>org.apache.tomcat.jdbc.pool.DataSourceFactory</code> + </p> + </subsection> + <subsection name="Standalone"> + <p> + The connection pool only has another dependency, and that is on tomcat-juli.jar. + To configure the pool in a stand alone project using bean instantiation, the bean to instantiate is + <code>org.apache.tomcat.jdbc.pool.DataSource</code>. The same attributes (documented below) as you use to configure a connection + pool as a JNDI resource, are used to configure a data source as a bean. + </p> + </subsection> + <subsection name="JMX"> + <p> + The connection pool object exposes an MBean that can be registered. + In order for the connection pool object to create the MBean, the flag <code>jmxEnabled</code> has to be set to true. + This doesn't imply that the pool will be registered with an MBean server, merely that the MBean is created. + In a container like Tomcat, Tomcat itself registers the DataSource with the MBean server, the + <code>org.apache.tomcat.jdbc.pool.DataSource</code> object will then register the actual + connection pool MBean. + If you're running outside of a container, you can register the DataSource yourself under any object name you specify, + and it propagates the registration to the underlying pool. To do this you would call <code>mBeanServer.registerMBean(dataSource.getPool().getJmxPool(),objectname)</code>. + Prior to this call, ensure that the pool has been created by calling <code>dataSource.createPool()</code>. + </p> + </subsection> + +</section> +<section name="Attributes"> + <p>To provide a very simple switch to and from commons-dbcp and tomcat-jdbc-pool, + Most attributes are the same and have the same meaning.</p> + <subsection name="JNDI Factory and Type"> + <attributes> + <attribute name="factory" required="true"> + <p>factory is required, and the value should be <code>org.apache.tomcat.jdbc.pool.DataSourceFactory</code></p> + </attribute> + <attribute name="type" required="true"> + <p>Type should always be <code>javax.sql.DataSource</code> or <code>javax.sql.XADataSource</code></p> + <p>Depending on the type a <code>org.apache.tomcat.jdbc.pool.DataSource</code> or a <code>org.apache.tomcat.jdbc.pool.XADataSource</code> will be created.</p> + </attribute> + </attributes> + </subsection> + + <subsection name="System Properties"> + <p>System properties are JVM wide, affect all pools created in the JVM</p> + <attributes> + <attribute name="org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader" required="false"> + <p>(boolean) Controls classloading of dynamic classes, such as + JDBC drivers, interceptors and validators. If set to + <code>false</code>, default value, the pool will first attempt + to load using the current loader (i.e. the class loader that + loaded the pool classes) and if class loading fails attempt to + load using the thread context loader. Set this value to + <code>true</code>, if you wish to remain backwards compatible + with Apache Tomcat 8.0.8 and earlier, and only attempt the + current loader. + If not set then the default value is <code>false</code>. + </p> + </attribute> + </attributes> + </subsection> + + <subsection name="Common Attributes"> + <p>These attributes are shared between commons-dbcp and tomcat-jdbc-pool, in some cases default values are different.</p> + <attributes> + + <attribute name="defaultAutoCommit" required="false"> + <p>(boolean) The default auto-commit state of connections created by this pool. If not set, default is JDBC driver default (If not set then the <code>setAutoCommit</code> method will not be called.)</p> + </attribute> + + <attribute name="defaultReadOnly" required="false"> + <p>(boolean) The default read-only state of connections created by this pool. If not set then the <code>setReadOnly</code> method will not be called. (Some drivers don't support read only mode, ex: Informix)</p> + </attribute> + + <attribute name="defaultTransactionIsolation" required="false"> + <p>(String) The default TransactionIsolation state of connections created by this pool. One of the following: (see javadoc )</p> + <ul> + <li><code>NONE</code></li> + <li><code>READ_COMMITTED</code></li> + <li><code>READ_UNCOMMITTED</code></li> + <li><code>REPEATABLE_READ</code></li> + <li><code>SERIALIZABLE</code></li> + </ul> + <p>If not set, the method will not be called and it defaults to the JDBC driver.</p> + </attribute> + + <attribute name="defaultCatalog" required="false"> + <p>(String) The default catalog of connections created by this pool.</p> + </attribute> + + <attribute name="driverClassName" required="true"> + <p>(String) The fully qualified Java class name of the JDBC driver to be used. The driver has to be accessible + from the same classloader as tomcat-jdbc.jar + </p> + </attribute> + + <attribute name="username" required="true"> + <p>(String) The connection username to be passed to our JDBC driver to establish a connection. + Note that method <code>DataSource.getConnection(username,password)</code> + by default will not use credentials passed into the method, + but will use the ones configured here. See <code>alternateUsernameAllowed</code> + property for more details. + </p> + </attribute> + + <attribute name="password" required="true"> + <p>(String) The connection password to be passed to our JDBC driver to establish a connection. + Note that method <code>DataSource.getConnection(username,password)</code> + by default will not use credentials passed into the method, + but will use the ones configured here. See <code>alternateUsernameAllowed</code> + property for more details. + </p> + </attribute> + + <attribute name="maxActive" required="false"> + <p>(int) The maximum number of active connections that can be allocated from this pool at the same time. + The default value is <code>100</code></p> + </attribute> + + <attribute name="maxIdle" required="false"> + <p>(int) The maximum number of connections that should be kept in the pool at all times. + Default value is <code>maxActive</code>:<code>100</code> + Idle connections are checked periodically (if enabled) and + connections that been idle for longer than <code>minEvictableIdleTimeMillis</code> + will be released. (also see <code>testWhileIdle</code>)</p> + </attribute> + + <attribute name="minIdle" required="false"> + <p> + (int) The minimum number of established connections that should be kept in the pool at all times. + The connection pool can shrink below this number if validation queries fail. + Default value is derived from <code>initialSize</code>:<code>10</code> (also see <code>testWhileIdle</code>) + </p> + </attribute> + + <attribute name="initialSize" required="false"> + <p>(int)The initial number of connections that are created when the pool is started. + Default value is <code>10</code></p> + </attribute> + + <attribute name="maxWait" required="false"> + <p>(int) The maximum number of milliseconds that the pool will wait (when there are no available connections) + for a connection to be returned before throwing an exception. + Default value is <code>30000</code> (30 seconds)</p> + </attribute> + + <attribute name="testOnBorrow" required="false"> + <p>(boolean) The indication of whether objects will be validated before being borrowed from the pool. + If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another. + NOTE - for a true value to have any effect, the <code>validationQuery</code> + or <code>validatorClassName</code> parameter must be set to a non-null string. + In order to have a more efficient validation, see <code>validationInterval</code>. + Default value is <code>false</code> + </p> + </attribute> + + <attribute name="testOnConnect" required="false"> + <p>(boolean) The indication of whether objects will be validated when a connection is first created. + If an object fails to validate, it will be throw <code>SQLException</code>. + NOTE - for a true value to have any effect, the <code>validationQuery</code>, <code>initSQL</code> + or <code>validatorClassName</code> parameter must be set to a non-null string. + Default value is <code>false</code> + </p> + </attribute> + + <attribute name="testOnReturn" required="false"> + <p>(boolean) The indication of whether objects will be validated before being returned to the pool. + NOTE - for a true value to have any effect, the <code>validationQuery</code> + or <code>validatorClassName</code> parameter must be set to a non-null string. + The default value is <code>false</code>. + </p> + </attribute> + + <attribute name="testWhileIdle" required="false"> + <p>(boolean) The indication of whether objects will be validated by the idle object evictor (if any). + If an object fails to validate, it will be dropped from the pool. + NOTE - for a true value to have any effect, the <code>validationQuery</code> + or <code>validatorClassName</code> parameter must be set to a non-null string. + The default value is <code>false</code> and this property has to be set in order for the + pool cleaner/test thread is to run (also see <code>timeBetweenEvictionRunsMillis</code>) + </p> + </attribute> + + <attribute name="validationQuery" required="false"> + <p>(String) The SQL query that will be used to validate connections from this pool before returning them to the caller. + If specified, this query does not have to return any data, it just can't throw a <code>SQLException</code>. + The default value is <code>null</code>. + Example values are <code>SELECT 1</code>(mysql), <code>select 1 from dual</code>(oracle), <code>SELECT 1</code>(MS Sql Server) + </p> + </attribute> + + <attribute name="validationQueryTimeout" required="false"> + <p>(int) The timeout in seconds before a connection validation queries fail. This works by calling + <code>java.sql.Statement.setQueryTimeout(seconds)</code> on the statement that executes the <code>validationQuery</code>. + The pool itself doesn't timeout the query, it is still up to the JDBC driver to enforce query timeouts. + A value less than or equal to zero will disable this feature. + The default value is <code>-1</code>. + </p> + </attribute> + + <attribute name="validatorClassName" required="false"> + <p>(String) The name of a class which implements the + <code>org.apache.tomcat.jdbc.pool.Validator</code> interface and + provides a no-arg constructor (may be implicit). If specified, the + class will be used to create a Validator instance which is then used + instead of any validation query to validate connections. The default + value is <code>null</code>. An example value is + <code>com.mycompany.project.SimpleValidator</code>. + </p> + </attribute> + + <attribute name="timeBetweenEvictionRunsMillis" required="false"> + <p>(int) The number of milliseconds to sleep between runs of the idle connection validation/cleaner thread. + This value should not be set under 1 second. It dictates how often we check for idle, abandoned connections, and how often + we validate idle connections. + The default value is <code>5000</code> (5 seconds). <br/> + </p> + </attribute> + + <attribute name="numTestsPerEvictionRun" required="false"> + <p>(int) Property not used in tomcat-jdbc-pool.</p> + </attribute> + + <attribute name="minEvictableIdleTimeMillis" required="false"> + <p>(int) The minimum amount of time an object may sit idle in the pool before it is eligible for eviction. + The default value is <code>60000</code> (60 seconds).</p> + </attribute> + + <attribute name="accessToUnderlyingConnectionAllowed" required="false"> + <p>(boolean) Property not used. Access can be achieved by calling <code>unwrap</code> on the pooled connection. + see <code>javax.sql.DataSource</code> interface, or call <code>getConnection</code> through reflection or + cast the object as <code>javax.sql.PooledConnection</code></p> + </attribute> + + <attribute name="removeAbandoned" required="false"> + <p>(boolean) Flag to remove abandoned connections if they exceed the <code>removeAbandonedTimeout</code>. + If set to true a connection is considered abandoned and eligible for removal if it has been in use + longer than the <code>removeAbandonedTimeout</code> Setting this to <code>true</code> can recover db connections from + applications that fail to close a connection. See also <code>logAbandoned</code> + The default value is <code>false</code>.</p> + </attribute> + + <attribute name="removeAbandonedTimeout" required="false"> + <p>(int) Timeout in seconds before an abandoned(in use) connection can be removed. + The default value is <code>60</code> (60 seconds). The value should be set to the longest running query your applications + might have.</p> + </attribute> + + <attribute name="logAbandoned" required="false"> + <p>(boolean) Flag to log stack traces for application code which abandoned a Connection. + Logging of abandoned Connections adds overhead for every Connection borrow because a stack trace has to be generated. + The default value is <code>false</code>.</p> + </attribute> + + <attribute name="connectionProperties" required="false"> + <p>(String) The connection properties that will be sent to our JDBC driver when establishing new connections. + Format of the string must be [propertyName=property;]* + NOTE - The "user" and "password" properties will be passed explicitly, so they do not need to be included here. + The default value is <code>null</code>.</p> + </attribute> + + <attribute name="poolPreparedStatements" required="false"> + <p>(boolean) Property not used.</p> + </attribute> + + <attribute name="maxOpenPreparedStatements" required="false"> + <p>(int) Property not used.</p> + </attribute> + + </attributes> + + </subsection> + + <subsection name="Tomcat JDBC Enhanced Attributes"> + + <attributes> + + <attribute name="initSQL" required="false"> + <p>(String) A custom query to be run when a connection is first created. + The default value is <code>null</code>.</p> + </attribute> + + <attribute name="jdbcInterceptors" required="false"> + <p>(String) A semicolon separated list of classnames extending + <code>org.apache.tomcat.jdbc.pool.JdbcInterceptor</code> class. + See <a href="#Configuring_JDBC_interceptors">Configuring JDBC interceptors</a> + below for more detailed description of syntaz and examples. + </p> + <p> + These interceptors will be inserted as an interceptor into the chain + of operations on a <code>java.sql.Connection</code> object. + The default value is <code>null</code>. + </p> + <p> + Predefined interceptors:<br/> + <code>org.apache.tomcat.jdbc.pool.interceptor.<br />ConnectionState</code> + - keeps track of auto commit, read only, catalog and transaction isolation level.<br/> + <code>org.apache.tomcat.jdbc.pool.interceptor.<br />StatementFinalizer</code> + - keeps track of opened statements, and closes them when the connection is returned to the pool. + </p> + <p> + More predefined interceptors are described in detail in the + <a href="#JDBC_interceptors">JDBC Interceptors section</a>. + </p> + </attribute> + + <attribute name="validationInterval" required="false"> + <p>(long) avoid excess validation, only run validation at most at this frequency - time in milliseconds. + If a connection is due for validation, but has been validated previously within this interval, it will not be validated again. + The default value is <code>3000</code> (3 seconds).</p> + </attribute> + + <attribute name="jmxEnabled" required="false"> + <p>(boolean) Register the pool with JMX or not. + The default value is <code>true</code>.</p> + </attribute> + + <attribute name="fairQueue" required="false"> + <p>(boolean) Set to true if you wish that calls to getConnection should be treated + fairly in a true FIFO fashion. This uses the <code>org.apache.tomcat.jdbc.pool.FairBlockingQueue</code> + implementation for the list of the idle connections. The default value is <code>true</code>. + This flag is required when you want to use asynchronous connection retrieval.<br/> + Setting this flag ensures that threads receive connections in the order they arrive.<br/> + During performance tests, there is a very large difference in how locks + and lock waiting is implemented. When <code>fairQueue=true</code> + there is a decision making process based on what operating system the system is running. + If the system is running on Linux (property <code>os.name=Linux</code>. + To disable this Linux specific behavior and still use the fair queue, simply add the property + <code>org.apache.tomcat.jdbc.pool.FairBlockingQueue.ignoreOS=true</code> to your system properties + before the connection pool classes are loaded. + </p> + </attribute> + + <attribute name="abandonWhenPercentageFull" required="false"> + <p>(int) Connections that have been abandoned (timed out) wont get closed and reported up unless + the number of connections in use are above the percentage defined by <code>abandonWhenPercentageFull</code>. + The value should be between 0-100. + The default value is <code>0</code>, which implies that connections are eligible for closure as soon + as <code>removeAbandonedTimeout</code> has been reached.</p> + </attribute> + + <attribute name="maxAge" required="false"> + <p>(long) Time in milliseconds to keep this connection. This attribute + works both when returning connection and when borrowing connection. + When a connection is borrowed from the pool, the pool will check to see + if the <code>now - time-when-connected > maxAge</code> has been reached + , and if so, it reconnects before borrow it. When a connection is + returned to the pool, the pool will check to see if the + <code>now - time-when-connected > maxAge</code> has been reached, and + if so, it closes the connection rather than returning it to the pool. + The default value is <code>0</code>, which implies that connections + will be left open and no age check will be done upon borrowing from the + pool and returning the connection to the pool.</p> + </attribute> + + <attribute name="useEquals" required="false"> + <p>(boolean) Set to true if you wish the <code>ProxyConnection</code> class to use <code>String.equals</code> and set to <code>false</code> + when you wish to use <code>==</code> when comparing method names. This property does not apply to added interceptors as those are configured individually. + The default value is <code>true</code>. + </p> + </attribute> + <attribute name="suspectTimeout" required="false"> + <p>(int) Timeout value in seconds. Default value is <code>0</code>.<br/> + Similar to to the <code>removeAbandonedTimeout</code> value but instead of treating the connection + as abandoned, and potentially closing the connection, this simply logs the warning if + <code>logAbandoned</code> is set to true. If this value is equal or less than 0, no suspect + checking will be performed. Suspect checking only takes place if the timeout value is larger than 0 and + the connection was not abandoned or if abandon check is disabled. If a connection is suspect a WARN message gets + logged and a JMX notification gets sent once. + </p> + </attribute> + <attribute name="rollbackOnReturn" required="false"> + <p>(boolean) If <code>autoCommit==false</code> then the pool can terminate the transaction by calling rollback on the connection as it is returned to the pool + Default value is <code>false</code>.<br/> + </p> + </attribute> + <attribute name="commitOnReturn" required="false"> + <p>(boolean) If <code>autoCommit==false</code> then the pool can complete the transaction by calling commit on the connection as it is returned to the pool + If <code>rollbackOnReturn==true</code> then this attribute is ignored. + Default value is <code>false</code>.<br/> + </p> + </attribute> + <attribute name="alternateUsernameAllowed" required="false"> + <p>(boolean) By default, the jdbc-pool will ignore the + <a href="http://docs.oracle.com/javase/6/docs/api/javax/sql/DataSource.html#getConnection(java.lang.String,%20java.lang.String)"><code>DataSource.getConnection(username,password)</code></a> + call, and simply return a previously pooled connection under the globally configured properties <code>username</code> and <code>password</code>, for performance reasons. + </p> + <p> + The pool can however be configured to allow use of different credentials + each time a connection is requested. To enable the functionality described in the + <a href="http://docs.oracle.com/javase/6/docs/api/javax/sql/DataSource.html#getConnection(java.lang.String,%20java.lang.String)"><code>DataSource.getConnection(username,password)</code></a> + call, simply set the property <code>alternateUsernameAllowed</code> + to <code>true</code>.<br /> + Should you request a connection with the credentials user1/password1 and the connection + was previously connected using different user2/password2, the connection will be closed, + and reopened with the requested credentials. This way, the pool size is still managed + on a global level, and not on a per schema level. <br/> + The default value is <code>false</code>.<br/> + This property was added as an enhancement to <a href="https://bz.apache.org/bugzilla/show_bug.cgi?id=50025">bug 50025</a>. + </p> + </attribute> + <attribute name="dataSource" required="false"> + <p>(javax.sql.DataSource) Inject a data source to the connection pool, and the pool will use the data source to retrieve connections instead of establishing them using the <code>java.sql.Driver</code> interface. + This is useful when you wish to pool XA connections or connections established using a data source instead of a connection string. Default value is <code>null</code> + </p> + </attribute> + <attribute name="dataSourceJNDI" required="false"> + <p>(String) The JNDI name for a data source to be looked up in JNDI and then used to establish connections to the database. See the <code>dataSource</code> attribute. Default value is <code>null</code> + </p> + </attribute> + <attribute name="useDisposableConnectionFacade" required="false"> + <p>(boolean) Set this to true if you wish to put a facade on your connection so that it cannot be reused after it has been closed. This prevents a thread holding on to a + reference of a connection it has already called closed on, to execute queries on it. Default value is <code>true</code>. + </p> + </attribute> + <attribute name="logValidationErrors" required="false"> + <p>(boolean) Set this to true to log errors during the validation phase to the log file. If set to true, errors will be logged as SEVERE. Default value is <code>false</code> for backwards compatibility. + </p> + </attribute> + <attribute name="propagateInterruptState" required="false"> + <p>(boolean) Set this to true to propagate the interrupt state for a thread that has been interrupted (not clearing the interrupt state). Default value is <code>false</code> for backwards compatibility. + </p> + </attribute> + <attribute name="ignoreExceptionOnPreLoad" required="false"> + <p>(boolean) Flag whether ignore error of connection creation while initializing the pool. + Set to true if you want to ignore error of connection creation while initializing the pool. + Set to false if you want to fail the initialization of the pool by throwing exception. + The default value is <code>false</code>. + </p> + </attribute> + + </attributes> + </subsection> +</section> +<section name="Advanced usage"> + <subsection name="JDBC interceptors"> + <p>To see an example of how to use an interceptor, take a look at + <code>org.apache.tomcat.jdbc.pool.interceptor.ConnectionState</code>. + This simple interceptor is a cache of three attributes, transaction isolation level, auto commit and read only state, + in order for the system to avoid not needed roundtrips to the database. + </p> + <p>Further interceptors will be added to the core of the pool as the need arises. Contributions are always welcome!</p> + <p>Interceptors are of course not limited to just <code>java.sql.Connection</code> but can be used to wrap any + of the results from a method invokation as well. You could build query performance analyzer that provides JMX notifications when a + query is running longer than the expected time.</p> + </subsection> + <subsection name="Configuring JDBC interceptors"> + <p>Configuring JDBC interceptors is done using the <b>jdbcInterceptors</b> property. + The property contains a list of semicolon separated class names. If the + classname is not fully qualified it will be prefixed with the + <code>org.apache.tomcat.jdbc.pool.interceptor.</code> prefix. + </p> + <p>Example:<br/> + <code> + jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; + org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer" + </code> + <br/> + is the same as + <br/> + <code> jdbcInterceptors="ConnectionState;StatementFinalizer"</code> + </p> + <p> + Interceptors can have properties as well. Properties for an interceptor + are specified within parentheses after the class name. Several properties + are separated by commas. + </p> + <p>Example:<br/> + <code> + jdbcInterceptors="ConnectionState;StatementFinalizer(useEquals=true)" + </code> + </p> + <p> + Extra whitespace characters around class names, property names and values + are ignored. + </p> + </subsection> + <subsection name="org.apache.tomcat.jdbc.pool.JdbcInterceptor"> + <p>Abstract base class for all interceptors, cannot be instantiated.</p> + <attributes> + <attribute name="useEquals" required="false"> + <p>(boolean) Set to true if you wish the <code>ProxyConnection</code> class to use <code>String.equals</code> and set to <code>false</code> + when you wish to use <code>==</code> when comparing method names. + The default value is <code>true</code>. + </p> + </attribute> + </attributes> + </subsection> + <subsection name="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState"> + <p>Caches the connection for the following attributes <code>autoCommit</code>, <code>readOnly</code>, + <code>transactionIsolation</code> and <code>catalog</code>. + It is a performance enhancement to avoid roundtrip to the database when getters are called or setters are called with an already set value. + </p> + <attributes> + </attributes> + </subsection> + <subsection name="org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"> + <p>Keeps track of all statements created using <code>createStatement</code>, <code>prepareStatement</code> or <code>prepareCall</code> + and closes these statements when the connection is returned to the pool. + </p> + <attributes> + <attribute name="trace" required="false"> + <p>(boolean as String) Enable tracing of unclosed statements. + When enabled and a connection is closed, and statements are not closed, + the interceptor will log all stack traces. + The default value is <code>false</code>. + </p> + </attribute> + </attributes> + </subsection> + <subsection name="org.apache.tomcat.jdbc.pool.interceptor.StatementCache"> + <p>Caches <code>PreparedStatement</code> and/or <code>CallableStatement</code> + instances on a connection. + </p> + <p>The statements are cached per connection. + The count limit is counted globally for all connections that belong to + the same pool. Once the count reaches <code>max</code>, subsequent + statements are not returned to the cache and are closed immediately. + </p> + <attributes> + <attribute name="prepared" required="false"> + <p>(boolean as String) Enable caching of <code>PreparedStatement</code> + instances created using <code>prepareStatement</code> calls. + The default value is <code>true</code>. + </p> + </attribute> + <attribute name="callable" required="false"> + <p>(boolean as String) Enable caching of <code>CallableStatement</code> + instances created using <code>prepareCall</code> calls. + The default value is <code>false</code>. + </p> + </attribute> + <attribute name="max" required="false"> + <p>(int as String) Limit on the count of cached statements across + the connection pool. + The default value is <code>50</code>. + </p> + </attribute> + </attributes> + </subsection> + <subsection name="org.apache.tomcat.jdbc.pool.interceptor.StatementDecoratorInterceptor"> + <p>See <bug>48392</bug>. Interceptor to wrap statements and result sets in order to prevent access to the actual connection + using the methods <code>ResultSet.getStatement().getConnection()</code> and <code>Statement.getConnection()</code> + </p> + <attributes> + </attributes> + </subsection> + <subsection name="org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor"> + <p>Automatically calls <code>java.sql.Statement.setQueryTimeout(seconds)</code> when a new statement is created. + The pool itself doesn't timeout the query, it is still up to the JDBC driver to enforce query timeouts. + </p> + <attributes> + <attribute name="queryTimeout" required="true"> + <p>(int as String) The number of seconds to set for the query timeout. + A value less than or equal to zero will disable this feature. + The default value is <code>1</code> seconds. + </p> + </attribute> + </attributes> + </subsection> + <subsection name="org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport"> + <p>Keeps track of query performance and issues log entries when queries exceed a time threshold of fail. + The log level used is <code>WARN</code> + </p> + <attributes> + <attribute name="threshold" required="false"> + <p>(int as String) The number of milliseconds a query has to exceed before issuing a log alert. + The default value is <code>1000</code> milliseconds. + </p> + </attribute> + <attribute name="maxQueries" required="false"> + <p>(int as String) The maximum number of queries to keep track of in order to preserve memory space. + A value less than or equal to 0 will disable this feature. + The default value is <code>1000</code>. + </p> + </attribute> + <attribute name="logSlow" required="false"> + <p>(boolean as String) Set to <code>true</code> if you wish to log slow queries. + The default value is <code>true</code>. + </p> + </attribute> + <attribute name="logFailed" required="false"> + <p>(boolean as String) Set to <code>true</code> if you wish to log failed queries. + The default value is <code>false</code>. + </p> + </attribute> + </attributes> + </subsection> + <subsection name="org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx"> + <p>Extends the <code>SlowQueryReport</code> and in addition to log entries it issues JMX notification + for monitoring tools to react to. Inherits all the attributes from its parent class. + This class uses Tomcat's JMX engine so it wont work outside of the Tomcat container. + By default, JMX notifications are sent through the ConnectionPool mbean if it is enabled. + The <code>SlowQueryReportJmx</code> can also register an MBean if <code>notifyPool=false</code> + </p> + <attributes> + <attribute name="notifyPool" required="false"> + <p>(boolean as String) Set to false if you want JMX notifications to go to the <code>SlowQueryReportJmx</code> MBean + The default value is <code>true</code>. + </p> + </attribute> + <attribute name="objectName" required="false"> + <p>(String) Define a valid <code>javax.management.ObjectName</code> string that will be used to register this object with the platform mbean server + The default value is <code>null</code> and the object will be registered using + tomcat.jdbc:type=org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx,name=the-name-of-the-pool + </p> + </attribute> + </attributes> + </subsection> + <subsection name="org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer"> + <p> + The abandoned timer starts when a connection is checked out from the pool. + This means if you have a 30second timeout and run 10x10second queries using the connection + it will be marked abandoned and potentially reclaimed depending on the <code>abandonWhenPercentageFull</code> + attribute. + Using this interceptor it will reset the checkout timer every time you perform an operation on the connection or execute a + query successfully. + </p> + <attributes> + </attributes> + </subsection> +</section> + +<section name="Code Example"> + <p>Other examples of Tomcat configuration for JDBC usage can be found <a href="http://tomcat.apache.org/tomcat-8.0-doc/jndi-datasource-examples-howto.html">in the Tomcat documentation</a>. </p> + <subsection name="Plain Ol' Java"> + <p>Here is a simple example of how to create and use a data source.</p> +<source><![CDATA[ import java.sql.Connection; + import java.sql.ResultSet; + import java.sql.Statement; + + import org.apache.tomcat.jdbc.pool.DataSource; + import org.apache.tomcat.jdbc.pool.PoolProperties; + + public class SimplePOJOExample { + + public static void main(String[] args) throws Exception { + PoolProperties p = new PoolProperties(); + p.setUrl("jdbc:mysql://localhost:3306/mysql"); + p.setDriverClassName("com.mysql.jdbc.Driver"); + p.setUsername("root"); + p.setPassword("password"); + p.setJmxEnabled(true); + p.setTestWhileIdle(false); + p.setTestOnBorrow(true); + p.setValidationQuery("SELECT 1"); + p.setTestOnReturn(false); + p.setValidationInterval(30000); + p.setTimeBetweenEvictionRunsMillis(30000); + p.setMaxActive(100); + p.setInitialSize(10); + p.setMaxWait(10000); + p.setRemoveAbandonedTimeout(60); + p.setMinEvictableIdleTimeMillis(30000); + p.setMinIdle(10); + p.setLogAbandoned(true); + p.setRemoveAbandoned(true); + p.setJdbcInterceptors( + "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+ + "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"); + DataSource datasource = new DataSource(); + datasource.setPoolProperties(p); + + Connection con = null; + try { + con = datasource.getConnection(); + Statement st = con.createStatement(); + ResultSet rs = st.executeQuery("select * from user"); + int cnt = 1; + while (rs.next()) { + System.out.println((cnt++)+". Host:" +rs.getString("Host")+ + " User:"+rs.getString("User")+" Password:"+rs.getString("Password")); + } + rs.close(); + st.close(); + } finally { + if (con!=null) try {con.close();}catch (Exception ignore) {} + } + } + + }]]></source> + </subsection> + <subsection name="As a Resource"> + <p>And here is an example on how to configure a resource for JNDI lookups</p> +<source><![CDATA[<Resource name="jdbc/TestDB" + auth="Container" + type="javax.sql.DataSource" + factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" + testWhileIdle="true" + testOnBorrow="true" + testOnReturn="false" + validationQuery="SELECT 1" + validationInterval="30000" + timeBetweenEvictionRunsMillis="30000" + maxActive="100" + minIdle="10" + maxWait="10000" + initialSize="10" + removeAbandonedTimeout="60" + removeAbandoned="true" + logAbandoned="true" + minEvictableIdleTimeMillis="30000" + jmxEnabled="true" + jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; + org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer" + username="root" + password="password" + driverClassName="com.mysql.jdbc.Driver" + url="jdbc:mysql://localhost:3306/mysql"/>]]></source> + + </subsection> + <subsection name="Asynchronous Connection Retrieval"> + <p> The Tomcat JDBC connection pool supports asynchronous connection retrieval without adding additional threads to the + pool library. It does this by adding a method to the data source called <code>Future<Connection> getConnectionAsync()</code>. + In order to use the async retrieval, two conditions must be met: + </p> + <ol> + <li>You must configure the <code>fairQueue</code> property to be <code>true</code>.</li> + <li>You will have to cast the data source to <code>org.apache.tomcat.jdbc.pool.DataSource</code></li> + </ol> + An example of using the async feature is show below. +<source><![CDATA[ Connection con = null; + try { + Future<Connection> future = datasource.getConnectionAsync(); + while (!future.isDone()) { + System.out.println("Connection is not yet available. Do some background work"); + try { + Thread.sleep(100); //simulate work + }catch (InterruptedException x) { + Thread.currentThread().interrupt(); + } + } + con = future.get(); //should return instantly + Statement st = con.createStatement(); + ResultSet rs = st.executeQuery("select * from user");]]></source> + + </subsection> + <subsection name="Interceptors"> + <p>Interceptors are a powerful way to enable, disable or modify functionality on a specific connection or its sub components. + There are many different use cases for when interceptors are useful. By default, and for performance reasons, the connection pool is stateless. + The only state the pool itself inserts are <code>defaultAutoCommit</code>, <code>defaultReadOnly</code>, <code>defaultTransactionIsolation</code>, <code>defaultCatalog</code> if + these are set. These 4 properties are only set upon connection creation. Should these properties be modified during the usage of the connection, + the pool itself will not reset them.</p> + <p>An interceptor has to extend the <code>org.apache.tomcat.jdbc.pool.JdbcInterceptor</code> class. This class is fairly simple, + You will need to have a no arg constructor</p> +<source><![CDATA[ public JdbcInterceptor() { + }]]></source> + <p> + When a connection is borrowed from the pool, the interceptor can initialize or in some other way react to the event by implementing the + </p> +<source><![CDATA[ public abstract void reset(ConnectionPool parent, PooledConnection con);]]></source> + <p> + method. This method gets called with two parameters, a reference to the connection pool itself <code>ConnectionPool parent</code> + and a reference to the underlying connection <code>PooledConnection con</code>. + </p> + <p> + When a method on the <code>java.sql.Connection</code> object is invoked, it will cause the + </p> +<source><![CDATA[ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable]]></source> + <p> + method to get invoked. The <code>Method method</code> is the actual method invoked, and <code>Object[] args</code> are the arguments. + To look at a very simple example, where we demonstrate how to make the invokation to <code>java.sql.Connection.close()</code> a noop + if the connection has been closed + </p> +<source><![CDATA[ if (CLOSE_VAL==method.getName()) { + if (isClosed()) return null; //noop for already closed. + } + return super.invoke(proxy,method,args);]]></source> + <p> + There is an observation being made. It is the comparison of the method name. One way to do this would be to do + <code>"close".equals(method.getName())</code>. + Above we see a direct reference comparison between the method name and <code>static final String</code> reference. + According to the JVM spec, method names and static final String end up in a shared constant pool, so the reference comparison should work. + One could of course do this as well: + </p> +<source><![CDATA[ if (compare(CLOSE_VAL,method)) { + if (isClosed()) return null; //noop for already closed. + } + return super.invoke(proxy,method,args);]]></source> + <p> + The <code>compare(String,Method)</code> will use the <code>useEquals</code> flag on an interceptor and do either reference comparison or + a string value comparison when the <code>useEquals=true</code> flag is set. + </p> + <p>Pool start/stop<br/> + When the connection pool is started or closed, you can be notifed. You will only be notified once per interceptor class + even though it is an instance method. and you will be notified using an interceptor currently not attached to a pool. + </p> +<source><![CDATA[ public void poolStarted(ConnectionPool pool) { + } + + public void poolClosed(ConnectionPool pool) { + }]]></source> + <p> + When overriding these methods, don't forget to call super if you are extending a class other than <code>JdbcInterceptor</code> + </p> + <p>Configuring interceptors<br/> + Interceptors are configured using the <code>jdbcInterceptors</code> property or the <code>setJdbcInterceptors</code> method. + An interceptor can have properties, and would be configured like this + </p> +<source><![CDATA[ String jdbcInterceptors= + "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState(useEquals=true,fast=yes)"]]></source> + + <p>Interceptor properties<br/> + Since interceptors can have properties, you need to be able to read the values of these properties within your + interceptor. Taking an example like the one above, you can override the <code>setProperties</code> method. + </p> +<source><![CDATA[ public void setProperties(Map<String, InterceptorProperty> properties) { + super.setProperties(properties); + final String myprop = "myprop"; + InterceptorProperty p1 = properties.get(myprop); + if (p1!=null) { + setMyprop(Long.parseLong(p1.getValue())); + } + }]]></source> + + </subsection> + <subsection name="Getting the actual JDBC connection"> + <p>Connection pools create wrappers around the actual connection in order to properly pool them. + We also create interceptors in these wrappers to be able to perform certain functions. + If there is a need to retrieve the actual connection, one can do so using the <code>javax.sql.PooledConnection</code> + interface. + </p> +<source><![CDATA[ Connection con = datasource.getConnection(); + Connection actual = ((javax.sql.PooledConnection)con).getConnection();]]></source> + + </subsection> + +</section> + +<section name="Building"> + <p>We build the JDBC pool code with 1.6, but it is backwards compatible down to 1.5 for runtime environment. For unit test, we use 1.6 and higher</p> + <p>Other examples of Tomcat configuration for JDBC usage can be found <a href="http://tomcat.apache.org/tomcat-8.0-doc/jndi-datasource-examples-howto.html">in the Tomcat documentation</a>. </p> + <subsection name="Building from source"> + <p>Building is pretty simple. The pool has a dependency on <code>tomcat-juli.jar</code> and in case you want the <code>SlowQueryReportJmx</code></p> +<source><![CDATA[ javac -classpath tomcat-juli.jar \ + -d . \ + org/apache/tomcat/jdbc/pool/*.java \ + org/apache/tomcat/jdbc/pool/interceptor/*.java \ + org/apache/tomcat/jdbc/pool/jmx/*.java]]></source> + <p> + A build file can be found in the Tomcat <a href="http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/">source repository</a>. + </p> + <p> + As a convenience, a build file is also included where a simple build command will generate all files needed. + </p> +<source> ant download (downloads dependencies) + ant build (compiles and generates .jar files) + ant dist (creates a release package) + ant test (runs tests, expects a test database to be setup)</source> + + <p> + The system is structured for a Maven build, but does generate release artifacts. Just the library itself. + </p> + </subsection> +</section> +</body> + +</document> 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- + Stylesheet that generates "package.html" for Javadoc tool + from jdbc-pool.xml documentation file. + It is based on "tomcat-docs" stylesheet, but it needs to avoid + generating complicated headers and footers, as those cannot be + digested by Javadoc tool and break layout of javadoc pages. +--> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + version="1.0"> + + + <!-- Output method --> + <xsl:output method="html" + encoding="UTF-8" + indent="no"/> + + + <!-- Defined parameters (overrideable) --> + <xsl:param name="relative-path" select="'.'"/> + <xsl:param name="void-image" select="'/images/void.gif'"/> + <xsl:param name="standalone" select="''"/> + <xsl:param name="buglink" select="'http://bz.apache.org/bugzilla/show_bug.cgi?id='"/> + <xsl:param name="revlink" select="'http://svn.apache.org/viewvc?view=rev&rev='"/> + + <!-- Defined variables (non-overrideable) --> + <xsl:variable name="body-bg" select="'#ffffff'"/> + <xsl:variable name="body-fg" select="'#000000'"/> + <xsl:variable name="body-link" select="'#525D76'"/> + <xsl:variable name="banner-bg" select="'#525D76'"/> + <xsl:variable name="banner-fg" select="'#ffffff'"/> + <xsl:variable name="sub-banner-bg" select="'#828DA6'"/> + <xsl:variable name="sub-banner-fg" select="'#ffffff'"/> + <xsl:variable name="source-color" select="'#023264'"/> + <xsl:variable name="attributes-color" select="'#023264'"/> + <xsl:variable name="table-th-bg" select="'#039acc'"/> + <xsl:variable name="table-td-bg" select="'#a0ddf0'"/> + + <!-- Process an entire document into an HTML page --> + <xsl:template match="document"> + <xsl:variable name="project" + select="document('project.xml')/project"/> + <html> + <head> + <title><xsl:value-of select="project/title"/> - <xsl:value-of select="properties/title"/></title> + </head> + + <body bgcolor="{$body-bg}" text="{$body-fg}" link="{$body-link}" + alink="{$body-link}" vlink="{$body-link}"> + + <h2><xsl:value-of select="properties/title"/>.</h2> + <xsl:apply-templates select="body/section"/> + </body> + </html> + + </xsl:template> + + + <!-- Process a documentation section --> + <xsl:template match="section"> + <xsl:variable name="name"> + <xsl:value-of select="@name"/> + </xsl:variable> + <table border="0" cellspacing="0" cellpadding="2"> + <!-- Section heading --> + <tr><td bgcolor="{$banner-bg}"> + <font color="{$banner-fg}" face="arial,helvetica.sanserif"> + <a name="{$name}"> + <strong><xsl:value-of select="@name"/></strong></a></font> + </td></tr> + <!-- Section body --> + <tr><td><blockquote> + <xsl:apply-templates/> + </blockquote></td></tr> + </table> + </xsl:template> + + + <!-- Process a documentation subsection --> + <xsl:template match="subsection"> + <xsl:variable name="name"> + <xsl:value-of select="@name"/> + </xsl:variable> + <table border="0" cellspacing="0" cellpadding="2"> + <!-- Subsection heading --> + <tr><td bgcolor="{$sub-banner-bg}"> + <font color="{$sub-banner-fg}" face="arial,helvetica.sanserif"> + <a name="{$name}"> + <strong><xsl:value-of select="@name"/></strong></a></font> + </td></tr> + <!-- Subsection body --> + <tr><td><blockquote> + <xsl:apply-templates/> + </blockquote></td></tr> + </table> + </xsl:template> + + + <!-- Process a source code example --> + <xsl:template match="source"> + <xsl:variable name="void"> + <xsl:value-of select="$relative-path"/><xsl:value-of select="$void-image"/> + </xsl:variable> + <div align="left"> + <table cellspacing="4" cellpadding="0" border="0"> + <tr> + <td bgcolor="{$source-color}" width="1" height="1"> + <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/> + </td> + <td bgcolor="{$source-color}" height="1"> + <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/> + </td> + <td bgcolor="{$source-color}" width="1" height="1"> + <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/> + </td> + </tr> + <tr> + <td bgcolor="{$source-color}" width="1"> + <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/> + </td> + <td bgcolor="#ffffff" height="1"><pre> + <xsl:value-of select="."/> + </pre></td> + <td bgcolor="{$source-color}" width="1"> + <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/> + </td> + </tr> + <tr> + <td bgcolor="{$source-color}" width="1" height="1"> + <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/> + </td> + <td bgcolor="{$source-color}" height="1"> + <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/> + </td> + <td bgcolor="{$source-color}" width="1" height="1"> + <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/> + </td> + </tr> + </table> + </div> + </xsl:template> + + + <!-- Process an attributes list with nested attribute elements --> + <xsl:template match="attributes"> + <table border="1" cellpadding="5"> + <tr> + <th width="15%" bgcolor="{$attributes-color}"> + <font color="#ffffff">Attribute</font> + </th> + <th width="85%" bgcolor="{$attributes-color}"> + <font color="#ffffff">Description</font> + </th> + </tr> + <xsl:for-each select="attribute"> + <tr> + <td align="left" valign="center"> + <xsl:if test="@required = 'true'"> + <strong><code><xsl:value-of select="@name"/></code></strong> + </xsl:if> + <xsl:if test="@required != 'true'"> + <code><xsl:value-of select="@name"/></code> + </xsl:if> + </td> + <td align="left" valign="center"> + <xsl:apply-templates/> + </td> + </tr> + </xsl:for-each> + </table> + </xsl:template> + + <!-- Process a properties list with nested property elements --> + <xsl:template match="properties"> + <table border="1" cellpadding="5"> + <tr> + <th width="15%" bgcolor="{$attributes-color}"> + <font color="#ffffff">Property</font> + </th> + <th width="85%" bgcolor="{$attributes-color}"> + <font color="#ffffff">Description</font> + </th> + </tr> + <xsl:for-each select="property"> + <tr> + <td align="left" valign="center"> + <code><xsl:value-of select="@name"/></code> + </td> + <td align="left" valign="center"> + <xsl:apply-templates/> + </td> + </tr> + </xsl:for-each> + </table> + </xsl:template> + + <!-- Fix relative links in printer friendly versions of the docs --> + <xsl:template match="a"> + <xsl:variable name="href" select="@href"/> + <xsl:choose> + <xsl:when test="$standalone = 'standalone'"> + <xsl:apply-templates/> + </xsl:when> + <xsl:when test="$href != ''"> + <a href="{$href}"><xsl:apply-templates/></a> + </xsl:when> + <xsl:otherwise> + <xsl:variable name="name" select="@name"/> + <a name="{$name}"><xsl:apply-templates/></a> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- Link to a bug report --> + <xsl:template match="bug"> + <xsl:variable name="link"><xsl:value-of select="$buglink"/><xsl:value-of select="text()"/></xsl:variable> + <a href="{$link}"><xsl:apply-templates/></a> + </xsl:template> + + <!-- Link to a SVN revision report --> + <xsl:template match="rev"> + <xsl:variable name="link"><xsl:value-of select="$revlink"/><xsl:value-of select="text()"/></xsl:variable> + <a href="{$link}"><xsl:apply-templates/></a> + </xsl:template> + + <!-- Process everything else by just passing it through --> + <xsl:template match="*|@*"> + <xsl:copy> + <xsl:apply-templates select="@*|*|text()"/> + </xsl:copy> + </xsl:template> + +</xsl:stylesheet> 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<project name="Apache Tomcat JDBC Pool Documentation" + href="http://tomcat.apache.org/"> + + <title>Apache Tomcat JDBC Pool</title> + + <logo href="/images/tomcat.gif"> + The Apache Tomcat Servlet/JSP Container + </logo> + + <body> + </body> + +</project> + 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.openecomp.sdnc.core</groupId> + <artifactId>dblib</artifactId> + <version>1.1.0-SNAPSHOT</version> + </parent> + <artifactId>dblib-common</artifactId> + <packaging>bundle</packaging> + <name>DBLIB Adaptor - Common</name> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.tomcat</groupId> + <artifactId>juli</artifactId> + <version>6.0.32</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.11</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.tomcat</groupId> + <artifactId>tomcat-dbcp</artifactId> + <version>8.0.14</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <version>1.3.152</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>equinoxSDK381</groupId> + <artifactId>org.eclipse.osgi</artifactId> + <version>${equinox.osgi.version}</version> + </dependency> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + <version>${mysql.connector.version}</version> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.7</source> + <target>1.7</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-Activator>org.openecomp.sdnc.sli.resource.common.CommonActivator</Bundle-Activator> + <Export-Package> + 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}</Export-Package> + <Import-Package>*</Import-Package> + <!-- + <Embed-Dependency>*;scope=compile;artifactId=commons-lang|commons-lang3</Embed-Dependency> + --> + <Embed-Transitive>true</Embed-Transitive> + </instructions> + <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation> + </configuration> + </plugin> + </plugins> + </build> +</project> 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: + * <pre><code> + * <Resource factory="org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory" + * name="jdbc/test" + * type="org.apache.derby.jdbc.ClientXADataSource" + * databaseName="sample" + * createDatabase="create" + * serverName="localhost" + * port="1527"/> + * </code></pre> + * + */ +public class GenericNamingResourcesFactory implements ObjectFactory { + private static final Log log = LogFactory.getLog(GenericNamingResourcesFactory.class); + + @Override + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception { + if ((obj == null) || !(obj instanceof Reference)) { + return null; + } + Reference ref = (Reference) obj; + Enumeration<RefAddr> refs = ref.getAll(); + + String type = ref.getClassName(); + Object o = + ClassLoaderUtil.loadClass( + type, + GenericNamingResourcesFactory.class.getClassLoader(), + Thread.currentThread().getContextClassLoader()) + .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<PooledConnection> busy; + + /** + * Contains all the idle connections + */ + private BlockingQueue<PooledConnection> 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<Runnable>()); + + /** + * 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<Connection> 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<PooledConnection> pcf = ((FairBlockingQueue<PooledConnection>)idle).pollAsync(); + return new ConnectionFuture(pcf); + } else if (idle instanceof MultiLockFairBlockingQueue<?>) { + Future<PooledConnection> pcf = ((MultiLockFairBlockingQueue<PooledConnection>)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<PooledConnection> 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<proxies.length; i++) { + try { + JdbcInterceptor interceptor = proxies[i].getInterceptorClass().newInstance(); + interceptor.setProperties(proxies[i].getProperties()); + interceptor.poolClosed(this); + }catch (Exception x) { + log.debug("Unable to inform interceptor of pool closure.",x); + } + } + } //closePool + + + /** + * Initialize the connection pool - called from the constructor + * @param properties PoolProperties - properties used to initialize the pool with + * @throws SQLException if initialization fails + */ + protected void init(PoolConfiguration properties) throws SQLException { + poolProperties = properties; + + //make sure the pool is properly configured + checkPoolConfiguration(properties); + + //make space for 10 extra in case we flow over a bit + busy = new LinkedBlockingQueue<>(); + //busy = new FairBlockingQueue<PooledConnection>(); + //make space for 10 extra in case we flow over a bit + if (properties.isFairQueue()) { + idle = new FairBlockingQueue<>(); + //idle = new MultiLockFairBlockingQueue<PooledConnection>(); + //idle = new LinkedTransferQueue<PooledConnection>(); + //idle = new ArrayBlockingQueue<PooledConnection>(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; i<proxies.length; i++) { + try { + if (log.isDebugEnabled()) { + log.debug("Creating interceptor instance of class:"+proxies[i].getInterceptorClass()); + } + JdbcInterceptor interceptor = proxies[i].getInterceptorClass().newInstance(); + interceptor.setProperties(proxies[i].getProperties()); + interceptor.poolStarted(this); + }catch (Exception x) { + log.error("Unable to inform interceptor of pool start.",x); + if (jmxPool!=null) jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_INIT, getStackTrace(x)); + close(true); + SQLException ex = new SQLException(); + ex.initCause(x); + throw ex; + } + } + + //initialize the pool with its initial set of members + PooledConnection[] initialPool = new PooledConnection[poolProperties.getInitialSize()]; + try { + for (int i = 0; i < initialPool.length; i++) { + initialPool[i] = this.borrowConnection(0, null, null); //don't wait, should be no contention + } //for + + } catch (SQLException x) { + log.error("Unable to create initial connections of pool.", x); + if (!poolProperties.isIgnoreExceptionOnPreLoad()) { + if (jmxPool!=null) jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_INIT, getStackTrace(x)); + close(true); + throw x; + } + } finally { + //return the members as idle to the pool + for (int i = 0; i < initialPool.length; i++) { + if (initialPool[i] != null) { + try {this.returnConnection(initialPool[i]);}catch(Exception x){/*NOOP*/} + } //end if + } //for + } //catch + + closed = false; + } + + public void checkPoolConfiguration(PoolConfiguration properties) { + //make sure the pool is properly configured + if (properties.getMaxActive()<1) { + log.warn("maxActive is smaller than 1, setting maxActive to: "+PoolProperties.DEFAULT_MAX_ACTIVE); + properties.setMaxActive(PoolProperties.DEFAULT_MAX_ACTIVE); + } + if (properties.getMaxActive()<properties.getInitialSize()) { + log.warn("initialSize is larger than maxActive, setting initialSize to: "+properties.getMaxActive()); + properties.setInitialSize(properties.getMaxActive()); + } + if (properties.getMinIdle()>properties.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()<properties.getMinIdle()) { + log.warn("maxIdle is smaller than minIdle, setting maxIdle to: "+properties.getMinIdle()); + properties.setMaxIdle(properties.getMinIdle()); + } + } + + public void initializePoolCleaner(PoolConfiguration properties) { + //if the evictor thread is supposed to run, start it now + if (properties.isPoolSweeperEnabled()) { + poolCleaner = new PoolCleaner(this, properties.getTimeBetweenEvictionRunsMillis()); + poolCleaner.start(); + } //end if + } + + public void terminatePoolCleaner() { + if (poolCleaner!= null) { + poolCleaner.stopRunning(); + poolCleaner = null; + } + } + + +//=============================================================================== +// CONNECTION POOLING IMPL LOGIC +//=============================================================================== + + /** + * thread safe way to abandon a connection + * signals a connection to be abandoned. + * this will disconnect the connection, and log the stack trace if logAbandoned=true + * @param con PooledConnection + */ + protected void abandon(PooledConnection con) { + if (con == null) + return; + try { + con.lock(); + String trace = con.getStackTrace(); + if (getPoolProperties().isLogAbandoned()) { + log.warn("Connection has been abandoned " + con + ":" + trace); + } + if (jmxPool!=null) { + jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_ABANDON, trace); + } + //release the connection + removeAbandonedCount.incrementAndGet(); + release(con); + } finally { + con.unlock(); + } + } + + /** + * Thread safe way to suspect a connection. Similar to + * {@link #abandon(PooledConnection)}, but instead of actually abandoning + * the connection, this will log a warning and set the suspect flag on the + * {@link PooledConnection} if logAbandoned=true + * + * @param con PooledConnection + */ + protected void suspect(PooledConnection con) { + if (con == null) + return; + if (con.isSuspect()) + return; + try { + con.lock(); + String trace = con.getStackTrace(); + if (getPoolProperties().isLogAbandoned()) { + log.warn("Connection has been marked suspect, possibly abandoned " + con + "["+(System.currentTimeMillis()-con.getTimestamp())+" ms.]:" + trace); + } + if (jmxPool!=null) { + jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.SUSPECT_ABANDONED_NOTIFICATION, trace); + } + con.setSuspect(true); + } finally { + con.unlock(); + } + } + + /** + * thread safe way to release a connection + * @param con PooledConnection + */ + protected void release(PooledConnection con) { + if (con == null) + return; + try { + con.lock(); + if (con.release()) { + //counter only decremented once + size.addAndGet(-1); + con.setHandler(null); + } + releasedCount.incrementAndGet(); + } finally { + con.unlock(); + } + // we've asynchronously reduced the number of connections + // we could have threads stuck in idle.poll(timeout) that will never be + // notified + if (waitcount.get() > 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 <code>true</code> if the connection TX termination succeeded + * otherwise <code>false</code> + */ + 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 <code>true</code> 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 <code>true</code> 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<PooledConnection> 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<PooledConnection> 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<PooledConnection> 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 <code>true</code> 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 <code>true</code> 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<Connection>, Runnable { + Future<PooledConnection> 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<PooledConnection> 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<PoolCleaner> 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<Timer> 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<Timer> { + @Override + public Timer run() { + return new Timer("Tomcat JDBC Pool Cleaner["+ System.identityHashCode(ConnectionPool.class.getClassLoader()) + ":"+ + System.currentTimeMillis() + "]", true); + } + } + + public static Set<TimerTask> getPoolCleaners() { + return Collections.<TimerTask>unmodifiableSet(cleaners); + } + + public long getPoolVersion() { + return poolVersion.get(); + } + + public static Timer getPoolTimer() { + return poolCleanTimer; + } + + protected static class PoolCleaner extends TimerTask { + protected WeakReference<ConnectionPool> 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.<br> + * {@inheritDoc} + */ + @Override + public void postDeregister() { + if (oname!=null) unregisterJmx(); + } + + /** + * no-op<br> + * {@inheritDoc} + */ + @Override + public void postRegister(Boolean registrationDone) { + // NOOP + } + + + /** + * no-op<br> + * {@inheritDoc} + */ + @Override + public void preDeregister() throws Exception { + // NOOP + } + + /** + * If the connection pool MBean exists, it will be registered during this operation.<br> + * {@inheritDoc} + */ + @Override + public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { + try { + if ( isJmxEnabled() ) { + this.oname = createObjectName(name); + if (oname!=null) registerJmx(); + } + }catch (MalformedObjectNameException x) { + log.error("Unable to create object name for JDBC pool.",x); + } + return name; + } + + /** + * Creates the ObjectName for the ConnectionPoolMBean object to be registered + * @param original the ObjectName for the DataSource + * @return the ObjectName for the ConnectionPoolMBean + * @throws MalformedObjectNameException Invalid object name + */ + public ObjectName createObjectName(ObjectName original) throws MalformedObjectNameException { + String domain = ConnectionPool.POOL_JMX_DOMAIN; + Hashtable<String,String> properties = original.getKeyPropertyList(); + String origDomain = original.getDomain(); + properties.put("type", "ConnectionPool"); + properties.put("class", this.getClass().getName()); + if (original.getKeyProperty("path")!=null || properties.get("context")!=null) { + //this ensures that if the registration came from tomcat, we're not losing + //the unique domain, but putting that into as an engine attribute + properties.put("engine", origDomain); + } + ObjectName name = new ObjectName(domain,properties); + return name; + } + + /** + * Registers the ConnectionPoolMBean under a unique name based on the ObjectName for the DataSource + */ + protected void registerJmx() { + 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; + +/** + * <p>JNDI object factory that creates an instance of + * <code>BasicDataSource</code> that has been configured based on the + * <code>RefAddr</code> values of the specified <code>Reference</code>, + * which must match the names and data types of the + * <code>BasicDataSource</code> bean properties.</p> + * <br> + * Properties available for configuration:<br> + * <a href="http://commons.apache.org/dbcp/configuration.html">Commons DBCP properties</a><br> + *<ol> + * <li>initSQL - A query that gets executed once, right after the connection is established.</li> + * <li>testOnConnect - run validationQuery after connection has been established.</li> + * <li>validationInterval - avoid excess validation, only run validation at most at this frequency - time in milliseconds.</li> + * <li>jdbcInterceptors - a semicolon separated list of classnames extending {@link JdbcInterceptor} class.</li> + * <li>jmxEnabled - true of false, whether to register the pool with JMX.</li> + * <li>fairQueue - true of false, whether the pool should sacrifice a little bit of performance for true fairness.</li> + *</ol> + * @author Craig R. McClanahan + * @author Dirk Verbeeck + */ +public class DataSourceFactory implements ObjectFactory { + private static final Log log = LogFactory.getLog(DataSourceFactory.class); + + protected static final String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit"; + protected static final String PROP_DEFAULTREADONLY = "defaultReadOnly"; + protected static final String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation"; + protected static final String PROP_DEFAULTCATALOG = "defaultCatalog"; + + protected static final String PROP_DRIVERCLASSNAME = "driverClassName"; + protected static final String PROP_PASSWORD = "password"; + protected static final String PROP_URL = "url"; + protected static final String PROP_USERNAME = "username"; + + protected static final String PROP_MAXACTIVE = "maxActive"; + protected static final String PROP_MAXIDLE = "maxIdle"; + protected static final String PROP_MINIDLE = "minIdle"; + protected static final String PROP_INITIALSIZE = "initialSize"; + protected static final String PROP_MAXWAIT = "maxWait"; + protected static final String PROP_MAXAGE = "maxAge"; + + protected static final String PROP_TESTONBORROW = "testOnBorrow"; + protected static final String PROP_TESTONRETURN = "testOnReturn"; + protected static final String PROP_TESTWHILEIDLE = "testWhileIdle"; + protected static final String PROP_TESTONCONNECT = "testOnConnect"; + protected static final String PROP_VALIDATIONQUERY = "validationQuery"; + protected static final String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout"; + protected static final String PROP_VALIDATOR_CLASS_NAME = "validatorClassName"; + + protected static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun"; + protected static final String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis"; + protected static final String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis"; + + protected static final String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed"; + + protected static final String PROP_REMOVEABANDONED = "removeAbandoned"; + protected static final String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout"; + protected static final String PROP_LOGABANDONED = "logAbandoned"; + protected static final String PROP_ABANDONWHENPERCENTAGEFULL = "abandonWhenPercentageFull"; + + protected static final String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements"; + protected static final String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements"; + protected static final String PROP_CONNECTIONPROPERTIES = "connectionProperties"; + + protected static final String PROP_INITSQL = "initSQL"; + protected static final String PROP_INTERCEPTORS = "jdbcInterceptors"; + protected static final String PROP_VALIDATIONINTERVAL = "validationInterval"; + protected static final String PROP_JMX_ENABLED = "jmxEnabled"; + protected static final String PROP_FAIR_QUEUE = "fairQueue"; + + protected static final String PROP_USE_EQUALS = "useEquals"; + protected static final String PROP_USE_CON_LOCK = "useLock"; + + protected static final String PROP_DATASOURCE= "dataSource"; + protected static final String PROP_DATASOURCE_JNDI = "dataSourceJNDI"; + + protected static final String PROP_SUSPECT_TIMEOUT = "suspectTimeout"; + + protected static final String PROP_ALTERNATE_USERNAME_ALLOWED = "alternateUsernameAllowed"; + + protected static final String PROP_COMMITONRETURN = "commitOnReturn"; + protected static final String PROP_ROLLBACKONRETURN = "rollbackOnReturn"; + + protected static final String PROP_USEDISPOSABLECONNECTIONFACADE = "useDisposableConnectionFacade"; + + protected static final String PROP_LOGVALIDATIONERRORS = "logValidationErrors"; + + protected static final String PROP_PROPAGATEINTERRUPTSTATE = "propagateInterruptState"; + + protected static final String PROP_IGNOREEXCEPTIONONPRELOAD = "ignoreExceptionOnPreLoad"; + + 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 + + /** + * <p>Create and return a new <code>BasicDataSource</code> instance. If no + * instance can be created, return <code>null</code> instead.</p> + * + * @param obj The possibly null object containing location or + * reference information that can be used in creating an object + * @param name The name of this object relative to <code>nameCtx</code> + * @param nameCtx The context relative to which the <code>name</code> + * parameter is specified, or <code>null</code> if <code>name</code> + * is relative to the default initial context + * @param environment The possibly null environment that is used in + * creating this object + * + * @exception Exception if an exception occurs creating the instance + */ + @Override + public Object getObjectInstance(Object obj, Name name, Context nameCtx, + Hashtable<?,?> environment) throws Exception { + + // We only know how to deal with <code>javax.naming.Reference</code>s + // that specify a class name of "javax.sql.DataSource" + if ((obj == null) || !(obj instanceof Reference)) { + return null; + } + Reference ref = (Reference) obj; + boolean XA = false; + boolean ok = false; + if ("javax.sql.DataSource".equals(ref.getClassName())) { + ok = true; + } + if ("javax.sql.XADataSource".equals(ref.getClassName())) { + ok = true; + XA = true; + } + if (org.apache.tomcat.jdbc.pool.DataSource.class.getName().equals(ref.getClassName())) { + ok = true; + } + + if (!ok) { + log.warn(ref.getClassName()+" is not a valid class name/type for this JNDI factory."); + return null; + } + + + Properties properties = new Properties(); + for (int i = 0; i < ALL_PROPERTIES.length; i++) { + String propertyName = ALL_PROPERTIES[i]; + RefAddr ra = ref.get(propertyName); + if (ra != null) { + String propertyValue = ra.getContent().toString(); + properties.setProperty(propertyName, propertyValue); + } + } + + return createDataSource(properties,nameCtx,XA); + } + + public static PoolConfiguration parsePoolProperties(Properties properties) { + PoolConfiguration poolProperties = new PoolProperties(); + String value = null; + + value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT); + if (value != null) { + poolProperties.setDefaultAutoCommit(Boolean.valueOf(value)); + } + + value = properties.getProperty(PROP_DEFAULTREADONLY); + if (value != null) { + poolProperties.setDefaultReadOnly(Boolean.valueOf(value)); + } + + value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION); + if (value != null) { + int level = UNKNOWN_TRANSACTIONISOLATION; + if ("NONE".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_NONE; + } else if ("READ_COMMITTED".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_READ_COMMITTED; + } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_READ_UNCOMMITTED; + } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_REPEATABLE_READ; + } else if ("SERIALIZABLE".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_SERIALIZABLE; + } else { + try { + level = Integer.parseInt(value); + } catch (NumberFormatException e) { + System.err.println("Could not parse defaultTransactionIsolation: " + value); + System.err.println("WARNING: defaultTransactionIsolation not set"); + System.err.println("using default value of database driver"); + level = UNKNOWN_TRANSACTIONISOLATION; + } + } + poolProperties.setDefaultTransactionIsolation(level); + } + + value = properties.getProperty(PROP_DEFAULTCATALOG); + if (value != null) { + poolProperties.setDefaultCatalog(value); + } + + value = properties.getProperty(PROP_DRIVERCLASSNAME); + if (value != null) { + poolProperties.setDriverClassName(value); + } + + value = properties.getProperty(PROP_MAXACTIVE); + if (value != null) { + poolProperties.setMaxActive(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MAXIDLE); + if (value != null) { + poolProperties.setMaxIdle(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MINIDLE); + if (value != null) { + poolProperties.setMinIdle(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_INITIALSIZE); + if (value != null) { + poolProperties.setInitialSize(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MAXWAIT); + if (value != null) { + poolProperties.setMaxWait(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_TESTONBORROW); + if (value != null) { + poolProperties.setTestOnBorrow(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_TESTONRETURN); + if (value != null) { + poolProperties.setTestOnReturn(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_TESTONCONNECT); + if (value != null) { + poolProperties.setTestOnConnect(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS); + if (value != null) { + poolProperties.setTimeBetweenEvictionRunsMillis(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN); + if (value != null) { + poolProperties.setNumTestsPerEvictionRun(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS); + if (value != null) { + poolProperties.setMinEvictableIdleTimeMillis(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_TESTWHILEIDLE); + if (value != null) { + poolProperties.setTestWhileIdle(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_PASSWORD); + if (value != null) { + poolProperties.setPassword(value); + } + + value = properties.getProperty(PROP_URL); + if (value != null) { + poolProperties.setUrl(value); + } + + value = properties.getProperty(PROP_USERNAME); + if (value != null) { + poolProperties.setUsername(value); + } + + value = properties.getProperty(PROP_VALIDATIONQUERY); + if (value != null) { + poolProperties.setValidationQuery(value); + } + + value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT); + if (value != null) { + poolProperties.setValidationQueryTimeout(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_VALIDATOR_CLASS_NAME); + if (value != null) { + poolProperties.setValidatorClassName(value); + } + + value = properties.getProperty(PROP_VALIDATIONINTERVAL); + if (value != null) { + poolProperties.setValidationInterval(Long.parseLong(value)); + } + + value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED); + if (value != null) { + poolProperties.setAccessToUnderlyingConnectionAllowed(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_REMOVEABANDONED); + if (value != null) { + poolProperties.setRemoveAbandoned(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT); + if (value != null) { + poolProperties.setRemoveAbandonedTimeout(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_LOGABANDONED); + if (value != null) { + poolProperties.setLogAbandoned(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS); + if (value != null) { + log.warn(PROP_POOLPREPAREDSTATEMENTS + " is not a valid setting, it will have no effect."); + } + + value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS); + if (value != null) { + log.warn(PROP_MAXOPENPREPAREDSTATEMENTS + " is not a valid setting, it will have no effect."); + } + + value = properties.getProperty(PROP_CONNECTIONPROPERTIES); + if (value != null) { + Properties p = getProperties(value); + poolProperties.setDbProperties(p); + } else { + poolProperties.setDbProperties(new Properties()); + } + + if (poolProperties.getUsername()!=null) { + poolProperties.getDbProperties().setProperty("user",poolProperties.getUsername()); + } + if (poolProperties.getPassword()!=null) { + poolProperties.getDbProperties().setProperty("password",poolProperties.getPassword()); + } + + value = properties.getProperty(PROP_INITSQL); + if (value != null) { + poolProperties.setInitSQL(value); + } + + value = properties.getProperty(PROP_INTERCEPTORS); + if (value != null) { + poolProperties.setJdbcInterceptors(value); + } + + value = properties.getProperty(PROP_JMX_ENABLED); + if (value != null) { + poolProperties.setJmxEnabled(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_FAIR_QUEUE); + if (value != null) { + poolProperties.setFairQueue(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_USE_EQUALS); + if (value != null) { + poolProperties.setUseEquals(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(OBJECT_NAME); + if (value != null) { + poolProperties.setName(ObjectName.quote(value)); + } + + value = properties.getProperty(PROP_ABANDONWHENPERCENTAGEFULL); + if (value != null) { + poolProperties.setAbandonWhenPercentageFull(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MAXAGE); + if (value != null) { + poolProperties.setMaxAge(Long.parseLong(value)); + } + + value = properties.getProperty(PROP_USE_CON_LOCK); + if (value != null) { + poolProperties.setUseLock(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_DATASOURCE); + if (value != null) { + //this should never happen + throw new IllegalArgumentException("Can't set dataSource property as a string, this must be a javax.sql.DataSource object."); + + } + + value = properties.getProperty(PROP_DATASOURCE_JNDI); + if (value != null) { + poolProperties.setDataSourceJNDI(value); + } + + value = properties.getProperty(PROP_SUSPECT_TIMEOUT); + if (value != null) { + poolProperties.setSuspectTimeout(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_ALTERNATE_USERNAME_ALLOWED); + if (value != null) { + poolProperties.setAlternateUsernameAllowed(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_COMMITONRETURN); + if (value != null) { + poolProperties.setCommitOnReturn(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_ROLLBACKONRETURN); + if (value != null) { + poolProperties.setRollbackOnReturn(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_USEDISPOSABLECONNECTIONFACADE); + if (value != null) { + poolProperties.setUseDisposableConnectionFacade(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_LOGVALIDATIONERRORS); + if (value != null) { + poolProperties.setLogValidationErrors(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_PROPAGATEINTERRUPTSTATE); + if (value != null) { + poolProperties.setPropagateInterruptState(Boolean.parseBoolean(value)); + } + + value = properties.getProperty(PROP_IGNOREEXCEPTIONONPRELOAD); + if (value != null) { + poolProperties.setIgnoreExceptionOnPreLoad(Boolean.parseBoolean(value)); + } + + 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. + * <br> + * 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> T unwrap(Class<T> 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<Connection> 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: + * <ul> + * <li>{@link java.sql.Connection#close()} - returns the connection to the + * pool then breaks the link between cutoff and the next interceptor. + * May be called multiple times.</li> + * <li>{@link java.lang.Object#toString()} - returns a custom string for this + * object</li> + * </ul> + * By default method comparisons is done on a String reference level, unless the + * {@link PoolConfiguration#setUseEquals(boolean)} has been called with a + * <code>true</code> argument. + */ +public class DisposableConnectionFacade extends JdbcInterceptor { + protected DisposableConnectionFacade(JdbcInterceptor interceptor) { + setUseEquals(interceptor.isUseEquals()); + setNext(interceptor); + } + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + } + + + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + public boolean equals(Object obj) { + return this==obj; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + if (compare(EQUALS_VAL, method)) { + return Boolean.valueOf( + this.equals(Proxy.getInvocationHandler(args[0]))); + } else if (compare(HASHCODE_VAL, method)) { + return Integer.valueOf(this.hashCode()); + } else if (getNext()==null) { + if (compare(ISCLOSED_VAL, method)) { + return Boolean.TRUE; + } + else if (compare(CLOSE_VAL, method)) { + return null; + } + else if (compare(ISVALID_VAL, method)) { + return Boolean.FALSE; + } + } + + try { + return super.invoke(proxy, method, args); + } catch (NullPointerException e) { + if (getNext() == null) { + if (compare(TOSTRING_VAL, method)) { + return "DisposableConnectionFacade[null]"; + } + throw new SQLException( + "PooledConnection has already been closed."); + } + + throw e; + } finally { + if (compare(CLOSE_VAL, method)) { + setNext(null); + } + } + } +} 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. + * <br> + * Not all of the methods of the {@link java.util.concurrent.BlockingQueue} are implemented. + * + * @param <E> Type of element in the queue + */ + +public class FairBlockingQueue<E> implements BlockingQueue<E> { + + /** + * This little sucker is used to reorder the way to do + * {@link java.util.concurrent.locks.Lock#lock()}, + * {@link java.util.concurrent.locks.Lock#unlock()} + * and + * {@link java.util.concurrent.CountDownLatch#countDown()} + * during the {@link #poll(long, TimeUnit)} operation. + * On Linux, it performs much better if we count down while we hold the global + * lock, on Solaris its the other way around. + * Until we have tested other platforms we only check for Linux. + */ + static final boolean isLinux = "Linux".equals(System.getProperty("os.name")) && + (!Boolean.getBoolean(FairBlockingQueue.class.getName()+".ignoreOS")); + + /** + * Phase one entry lock in order to give out + * per-thread-locks for the waiting phase we have + * a phase one lock during the contention period. + */ + final ReentrantLock lock = new ReentrantLock(false); + + /** + * All the objects in the pool are stored in a simple linked list + */ + final LinkedList<E> items; + + /** + * All threads waiting for an object are stored in a linked list + */ + final LinkedList<ExchangeCountDownLatch<E>> waiters; + + /** + * Creates a new fair blocking queue. + */ + public FairBlockingQueue() { + items = new LinkedList<>(); + waiters = new LinkedList<>(); + } + + //------------------------------------------------------------------ + // USED BY CONPOOL IMPLEMENTATION + //------------------------------------------------------------------ + /** + * Will always return true, queue is unbounded. + * {@inheritDoc} + */ + @Override + public boolean offer(E e) { + //during the offer, we will grab the main lock + final ReentrantLock lock = this.lock; + lock.lock(); + ExchangeCountDownLatch<E> c = null; + try { + //check to see if threads are waiting for an object + if (waiters.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<E> c = new ExchangeCountDownLatch<>(1); + //add to the bottom of the wait list + waiters.addLast(c); + //unlock the global lock + lock.unlock(); + boolean didtimeout = true; + InterruptedException interruptedException = null; + try { + //wait for the specified timeout + didtimeout = !c.await(timeout, unit); + } catch (InterruptedException ix) { + interruptedException = ix; + } + if (didtimeout) { + //if we timed out, or got interrupted + // remove ourselves from the waitlist + lock.lock(); + try { + waiters.remove(c); + } finally { + lock.unlock(); + } + } + //return the item we received, can be null if we timed out + result = c.getItem(); + if (null!=interruptedException) { + //we got interrupted + if ( null!=result) { + //we got a result - clear the interrupt status + //don't propagate cause we have removed a connection from pool + Thread.interrupted(); + } else { + throw interruptedException; + } + } + } else { + //we have an object, release + lock.unlock(); + } + } finally { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + return result; + } + + /** + * Request an item from the queue asynchronously + * @return - a future pending the result from the queue poll request + */ + public Future<E> pollAsync() { + Future<E> result = null; + final ReentrantLock lock = this.lock; + //grab the global lock + lock.lock(); + try { + //check to see if we have objects in the queue + E item = items.poll(); + if (item==null) { + //queue is empty, add ourselves as waiters + ExchangeCountDownLatch<E> c = new ExchangeCountDownLatch<>(1); + waiters.addLast(c); + //return a future that will wait for the object + result = new ItemFuture<>(c); + } else { + //return a future with the item + result = new ItemFuture<>(item); + } + } finally { + lock.unlock(); + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean remove(Object e) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return items.remove(e); + } finally { + lock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + return items.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator<E> iterator() { + return new FairIterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public E poll() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return items.poll(); + } finally { + lock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Object e) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return items.contains(e); + } finally { + lock.unlock(); + } + } + + + //------------------------------------------------------------------ + // NOT USED BY CONPOOL IMPLEMENTATION + //------------------------------------------------------------------ + /** + * {@inheritDoc} + */ + @Override + public boolean add(E e) { + return offer(e); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public int drainTo(Collection<? super E> c, int maxElements) { + throw new UnsupportedOperationException("int drainTo(Collection<? super E> c, int maxElements)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + + @Override + public int drainTo(Collection<? super E> c) { + return drainTo(c,Integer.MAX_VALUE); + } + + /** + * {@inheritDoc} + */ + @Override + public void put(E e) throws InterruptedException { + offer(e); + } + + /** + * {@inheritDoc} + */ + @Override + public int remainingCapacity() { + return Integer.MAX_VALUE - size(); + } + + /** + * {@inheritDoc} + */ + @Override + public E take() throws InterruptedException { + return this.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean addAll(Collection<? extends E> c) { + Iterator<? extends E> i = c.iterator(); + while (i.hasNext()) { + E e = i.next(); + offer(e); + } + return true; + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public void clear() { + throw new UnsupportedOperationException("void clear()"); + + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean containsAll(Collection<?> c) { + throw new UnsupportedOperationException("boolean containsAll(Collection<?> c)"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEmpty() { + return size() == 0; + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean removeAll(Collection<?> c) { + throw new UnsupportedOperationException("boolean removeAll(Collection<?> c)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean retainAll(Collection<?> c) { + throw new UnsupportedOperationException("boolean retainAll(Collection<?> c)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public Object[] toArray() { + throw new UnsupportedOperationException("Object[] toArray()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public <T> T[] toArray(T[] a) { + throw new UnsupportedOperationException("<T> T[] toArray(T[] a)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E element() { + throw new UnsupportedOperationException("E element()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E peek() { + throw new UnsupportedOperationException("E peek()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E remove() { + throw new UnsupportedOperationException("E remove()"); + } + + + + //------------------------------------------------------------------ + // Non cancellable Future used to check and see if a connection has been made available + //------------------------------------------------------------------ + protected class ItemFuture<T> implements Future<T> { + protected volatile T item = null; + protected volatile ExchangeCountDownLatch<T> latch = null; + protected volatile boolean canceled = false; + + public ItemFuture(T item) { + this.item = item; + } + + public ItemFuture(ExchangeCountDownLatch<T> latch) { + this.latch = latch; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; //don't allow cancel for now + } + + @Override + public T get() throws InterruptedException, ExecutionException { + if (item!=null) { + return item; + } else if (latch!=null) { + latch.await(); + return latch.getItem(); + } else { + throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception()); + } + } + + @Override + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + if (item!=null) { + return item; + } else if (latch!=null) { + boolean timedout = !latch.await(timeout, unit); + if (timedout) throw new TimeoutException(); + else return latch.getItem(); + } else { + throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception()); + } + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return (item!=null || latch.getItem()!=null); + } + + } + + //------------------------------------------------------------------ + // Count down latch that can be used to exchange information + //------------------------------------------------------------------ + protected class ExchangeCountDownLatch<T> extends CountDownLatch { + protected volatile T item; + public ExchangeCountDownLatch(int i) { + super(i); + } + public T getItem() { + return item; + } + public void setItem(T item) { + this.item = item; + } + } + + //------------------------------------------------------------------ + // Iterator safe from concurrent modification exceptions + //------------------------------------------------------------------ + protected class FairIterator implements Iterator<E> { + E[] elements = null; + int index; + E element = null; + + @SuppressWarnings("unchecked") // Can't create arrays of generic types + public FairIterator() { + final ReentrantLock lock = FairBlockingQueue.this.lock; + lock.lock(); + try { + elements = (E[]) new Object[FairBlockingQueue.this.items.size()]; + FairBlockingQueue.this.items.toArray(elements); + index = 0; + } finally { + lock.unlock(); + } + } + @Override + public boolean hasNext() { + return index<elements.length; + } + + @Override + public E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + element = elements[index++]; + return element; + } + + @Override + public void remove() { + final ReentrantLock lock = FairBlockingQueue.this.lock; + lock.lock(); + try { + if (element!=null) { + FairBlockingQueue.this.items.remove(element); + } + } finally { + lock.unlock(); + } + } + + } +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java new file mode 100644 index 0000000..46bc0b1 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java @@ -0,0 +1,259 @@ +/*- + * ============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.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Map; + +import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty; + +/** + * Abstract class that is to be extended for implementations of interceptors. + * Everytime an operation is called on the {@link java.sql.Connection} object the + * {@link #invoke(Object, Method, Object[])} method on the interceptor will be called. + * Interceptors are useful to change or improve behavior of the connection pool.<br> + * Interceptors can receive a set of properties. Each sub class is responsible for parsing the properties during runtime when they + * are needed or simply override the {@link #setProperties(Map)} method. + * Properties arrive in a key-value pair of Strings as they were received through the configuration. + * This method is called once per cached connection object when the object is first configured. + * + * @version 1.0 + */ +public abstract class JdbcInterceptor implements InvocationHandler { + /** + * {@link java.sql.Connection#close()} method name + */ + public static final String CLOSE_VAL = "close"; + /** + * {@link Object#toString()} method name + */ + public static final String TOSTRING_VAL = "toString"; + /** + * {@link java.sql.Connection#isClosed()} method name + */ + public static final String ISCLOSED_VAL = "isClosed"; + /** + * {@link javax.sql.PooledConnection#getConnection()} method name + */ + public static final String GETCONNECTION_VAL = "getConnection"; + /** + * {@link java.sql.Wrapper#unwrap(Class)} method name + */ + public static final String UNWRAP_VAL = "unwrap"; + /** + * {@link java.sql.Wrapper#isWrapperFor(Class)} method name + */ + public static final String ISWRAPPERFOR_VAL = "isWrapperFor"; + + /** + * {@link java.sql.Connection#isValid(int)} method name + */ + public static final String ISVALID_VAL = "isValid"; + + /** + * {@link java.lang.Object#equals(Object)} + */ + public static final String EQUALS_VAL = "equals"; + + /** + * {@link java.lang.Object#hashCode()} + */ + public static final String HASHCODE_VAL = "hashCode"; + + /** + * Properties for this interceptor. + */ + protected Map<String,InterceptorProperty> properties = null; + + /** + * The next interceptor in the chain + */ + private volatile JdbcInterceptor next = null; + /** + * Property that decides how we do string comparison, default is to use + * {@link String#equals(Object)}. If set to <code>false</code> then the + * equality operator (==) is used. + */ + private boolean useEquals = true; + + /** + * Public constructor for instantiation through reflection + */ + public JdbcInterceptor() { + // NOOP + } + + /** + * Gets invoked each time an operation on {@link java.sql.Connection} is invoked. + * {@inheritDoc} + */ + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (getNext()!=null) return getNext().invoke(proxy,method,args); + else throw new NullPointerException(); + } + + /** + * Returns the next interceptor in the chain + * @return the next interceptor in the chain + */ + public JdbcInterceptor getNext() { + return next; + } + + /** + * configures the next interceptor in the chain + * @param next The next chain item + */ + public void setNext(JdbcInterceptor next) { + this.next = next; + } + + /** + * Performs a string comparison, using references unless the useEquals property is set to true. + * @param name1 The first name + * @param name2 The second name + * @return true if name1 is equal to name2 based on {@link #useEquals} + */ + public boolean compare(String name1, String name2) { + if (isUseEquals()) { + return name1.equals(name2); + } else { + return name1==name2; + } + } + + /** + * Compares a method name (String) to a method (Method) + * {@link #compare(String,String)} + * Uses reference comparison unless the useEquals property is set to true + * @param methodName The method name + * @param method The method + * @return <code>true</code> if the name matches + */ + public boolean compare(String methodName, Method method) { + return compare(methodName, method.getName()); + } + + /** + * Gets called each time the connection is borrowed from the pool + * This means that if an interceptor holds a reference to the connection + * the interceptor can be reused for another connection. + * <br> + * This method may be called with null as both arguments when we are closing down the connection. + * @param parent - the connection pool owning the connection + * @param con - the pooled connection + */ + public abstract void reset(ConnectionPool parent, PooledConnection con); + + /** + * Called when {@link java.sql.Connection#close()} is called on the underlying connection. + * This is to notify the interceptors, that the physical connection has been released. + * Implementation of this method should be thought through with care, as no actions should trigger an exception. + * @param parent - the connection pool that this connection belongs to + * @param con - the pooled connection that holds this connection + * @param finalizing - if this connection is finalizing. True means that the pooled connection will not reconnect the underlying connection + */ + public void disconnected(ConnectionPool parent, PooledConnection con, boolean finalizing) { + } + + + /** + * Returns the properties configured for this interceptor + * @return the configured properties for this interceptor + */ + public Map<String,InterceptorProperty> getProperties() { + return properties; + } + + /** + * Called during the creation of an interceptor + * The properties can be set during the configuration of an interceptor + * Override this method to perform type casts between string values and object properties + * @param properties The properties + */ + public void setProperties(Map<String,InterceptorProperty> properties) { + this.properties = properties; + final String useEquals = "useEquals"; + InterceptorProperty p = properties.get(useEquals); + if (p!=null) { + setUseEquals(Boolean.parseBoolean(p.getValue())); + } + } + + /** + * @return true if the compare method uses the Object.equals(Object) method + * false if comparison is done on a reference level + */ + public boolean isUseEquals() { + return useEquals; + } + + /** + * Set to true if string comparisons (for the {@link #compare(String, Method)} and {@link #compare(String, String)} methods) should use the Object.equals(Object) method + * The default is false + * @param useEquals <code>true</code> if equals will be used for comparisons + */ + public void setUseEquals(boolean useEquals) { + this.useEquals = useEquals; + } + + /** + * This method is invoked by a connection pool when the pool is closed. + * Interceptor classes can override this method if they keep static + * variables or other tracking means around. + * <b>This method is only invoked on a single instance of the interceptor, and not on every instance created.</b> + * @param pool - the pool that is being closed. + */ + public void poolClosed(ConnectionPool pool) { + // NOOP + } + + /** + * This method is invoked by a connection pool when the pool is first started up, usually when the first connection is requested. + * Interceptor classes can override this method if they keep static + * variables or other tracking means around. + * <b>This method is only invoked on a single instance of the interceptor, and not on every instance created.</b> + * @param pool - the pool that is being closed. + */ + public void poolStarted(ConnectionPool pool) { + // NOOP + } + +} 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; + +/** + * <b>EXPERIMENTAL AND NOT YET COMPLETE!</b> + * + * + * An implementation of a blocking queue with fairness waiting and lock dispersal to avoid contention. + * invocations to method poll(...) will get handed out in the order they were received. + * Locking is fine grained, a shared lock is only used during the first level of contention, waiting is done in a + * lock per thread basis so that order is guaranteed once the thread goes into a suspended monitor state. + * <br> + * Not all of the methods of the {@link java.util.concurrent.BlockingQueue} are implemented. + * + * @param <E> Type of element in the queue + */ + +public class MultiLockFairBlockingQueue<E> implements BlockingQueue<E> { + + final int LOCK_COUNT = Runtime.getRuntime().availableProcessors(); + + final AtomicInteger putQueue = new AtomicInteger(0); + final AtomicInteger pollQueue = new AtomicInteger(0); + + public int getNextPut() { + int idx = Math.abs(putQueue.incrementAndGet()) % LOCK_COUNT; + return idx; + } + + public int getNextPoll() { + int idx = Math.abs(pollQueue.incrementAndGet()) % LOCK_COUNT; + return idx; + } + /** + * Phase one entry lock in order to give out + * per-thread-locks for the waiting phase we have + * a phase one lock during the contention period. + */ + private final ReentrantLock[] locks = new ReentrantLock[LOCK_COUNT]; + + /** + * All the objects in the pool are stored in a simple linked list + */ + final LinkedList<E>[] items; + + /** + * All threads waiting for an object are stored in a linked list + */ + final LinkedList<ExchangeCountDownLatch<E>>[] waiters; + + /** + * Creates a new fair blocking queue. + */ + @SuppressWarnings("unchecked") // Can create arrays of generic types + public MultiLockFairBlockingQueue() { + items = new LinkedList[LOCK_COUNT]; + waiters = new LinkedList[LOCK_COUNT]; + for (int i=0; i<LOCK_COUNT; i++) { + items[i] = new LinkedList<>(); + waiters[i] = new LinkedList<>(); + locks[i] = new ReentrantLock(false); + } + } + + //------------------------------------------------------------------ + // USED BY CONPOOL IMPLEMENTATION + //------------------------------------------------------------------ + /** + * Will always return true, queue is unbounded. + * {@inheritDoc} + */ + @Override + public boolean offer(E e) { + int idx = getNextPut(); + //during the offer, we will grab the main lock + final ReentrantLock lock = this.locks[idx]; + lock.lock(); + ExchangeCountDownLatch<E> c = null; + try { + //check to see if threads are waiting for an object + if (waiters[idx].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<E> c = new ExchangeCountDownLatch<>(1); + //add to the bottom of the wait list + waiters[idx].addLast(c); + //unlock the global lock + lock.unlock(); + //wait for the specified timeout + if (!c.await(timeout, unit)) { + //if we timed out, remove ourselves from the waitlist + lock.lock(); + waiters[idx].remove(c); + lock.unlock(); + } + //return the item we received, can be null if we timed out + result = c.getItem(); + } else { + //we have an object, release + lock.unlock(); + } + } finally { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + return result; + } + + /** + * Request an item from the queue asynchronously + * @return - a future pending the result from the queue poll request + */ + public Future<E> pollAsync() { + int idx = getNextPoll(); + Future<E> result = null; + final ReentrantLock lock = this.locks[idx]; + try { + //grab the global lock + lock.lock(); + //check to see if we have objects in the queue + E item = items[idx].poll(); + if (item==null) { + //queue is empty, add ourselves as waiters + ExchangeCountDownLatch<E> c = new ExchangeCountDownLatch<>(1); + waiters[idx].addLast(c); + //return a future that will wait for the object + result = new ItemFuture<>(c); + } else { + //return a future with the item + result = new ItemFuture<>(item); + } + } finally { + lock.unlock(); + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean remove(Object e) { + for (int idx=0; idx<LOCK_COUNT; idx++) { + final ReentrantLock lock = this.locks[idx]; + lock.lock(); + try { + boolean result = items[idx].remove(e); + if (result) return result; + } finally { + lock.unlock(); + } + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + int size = 0; + for (int idx=0; idx<LOCK_COUNT; idx++) { + size += items[idx].size(); + } + return size; + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator<E> iterator() { + return new FairIterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public E poll() { + int idx = getNextPoll(); + final ReentrantLock lock = this.locks[idx]; + lock.lock(); + try { + return items[idx].poll(); + } finally { + lock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Object e) { + for (int idx=0; idx<LOCK_COUNT; idx++) { + boolean result = items[idx].contains(e); + if (result) return result; + } + return false; + } + + + //------------------------------------------------------------------ + // NOT USED BY CONPOOL IMPLEMENTATION + //------------------------------------------------------------------ + /** + * {@inheritDoc} + */ + @Override + public boolean add(E e) { + return offer(e); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public int drainTo(Collection<? super E> c, int maxElements) { + throw new UnsupportedOperationException("int drainTo(Collection<? super E> c, int maxElements)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public int drainTo(Collection<? super E> c) { + return drainTo(c,Integer.MAX_VALUE); + } + + /** + * {@inheritDoc} + */ + @Override + public void put(E e) throws InterruptedException { + offer(e); + } + + /** + * {@inheritDoc} + */ + @Override + public int remainingCapacity() { + return Integer.MAX_VALUE - size(); + } + + /** + * {@inheritDoc} + */ + @Override + public E take() throws InterruptedException { + return this.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean addAll(Collection<? extends E> c) { + Iterator<? extends E> i = c.iterator(); + while (i.hasNext()) { + E e = i.next(); + offer(e); + } + return true; + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public void clear() { + throw new UnsupportedOperationException("void clear()"); + + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean containsAll(Collection<?> c) { + throw new UnsupportedOperationException("boolean containsAll(Collection<?> c)"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEmpty() { + return size() == 0; + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean removeAll(Collection<?> c) { + throw new UnsupportedOperationException("boolean removeAll(Collection<?> c)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public boolean retainAll(Collection<?> c) { + throw new UnsupportedOperationException("boolean retainAll(Collection<?> c)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public Object[] toArray() { + throw new UnsupportedOperationException("Object[] toArray()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public <T> T[] toArray(T[] a) { + throw new UnsupportedOperationException("<T> T[] toArray(T[] a)"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E element() { + throw new UnsupportedOperationException("E element()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E peek() { + throw new UnsupportedOperationException("E peek()"); + } + + /** + * {@inheritDoc} + * @throws UnsupportedOperationException - this operation is not supported + */ + @Override + public E remove() { + throw new UnsupportedOperationException("E remove()"); + } + + + + //------------------------------------------------------------------ + // Non cancellable Future used to check and see if a connection has been made available + //------------------------------------------------------------------ + protected class ItemFuture<T> implements Future<T> { + protected volatile T item = null; + protected volatile ExchangeCountDownLatch<T> latch = null; + protected volatile boolean canceled = false; + + public ItemFuture(T item) { + this.item = item; + } + + public ItemFuture(ExchangeCountDownLatch<T> latch) { + this.latch = latch; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; //don't allow cancel for now + } + + @Override + public T get() throws InterruptedException, ExecutionException { + if (item!=null) { + return item; + } else if (latch!=null) { + latch.await(); + return latch.getItem(); + } else { + throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception()); + } + } + + @Override + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + if (item!=null) { + return item; + } else if (latch!=null) { + boolean timedout = !latch.await(timeout, unit); + if (timedout) throw new TimeoutException(); + else return latch.getItem(); + } else { + throw new ExecutionException("ItemFuture incorrectly instantiated. Bug in the code?", new Exception()); + } + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return (item!=null || latch.getItem()!=null); + } + + } + + //------------------------------------------------------------------ + // Count down latch that can be used to exchange information + //------------------------------------------------------------------ + protected class ExchangeCountDownLatch<T> extends CountDownLatch { + protected volatile T item; + public ExchangeCountDownLatch(int i) { + super(i); + } + public T getItem() { + return item; + } + public void setItem(T item) { + this.item = item; + } + } + + //------------------------------------------------------------------ + // Iterator safe from concurrent modification exceptions + //------------------------------------------------------------------ + protected class FairIterator implements Iterator<E> { + E[] elements = null; + int index; + E element = null; + + @SuppressWarnings("unchecked") // Can't create arrays of generic types + public FairIterator() { + ArrayList<E> list = new ArrayList<>(MultiLockFairBlockingQueue.this.size()); + for (int idx=0; idx<LOCK_COUNT; idx++) { + final ReentrantLock lock = MultiLockFairBlockingQueue.this.locks[idx]; + lock.lock(); + try { + elements = (E[]) new Object[MultiLockFairBlockingQueue.this.items[idx].size()]; + MultiLockFairBlockingQueue.this.items[idx].toArray(elements); + + } finally { + lock.unlock(); + } + } + index = 0; + elements = (E[]) new Object[list.size()]; + list.toArray(elements); + } + @Override + public boolean hasNext() { + return index<elements.length; + } + + @Override + public E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + element = elements[index++]; + return element; + } + + @Override + public void remove() { + for (int idx=0; idx<LOCK_COUNT; idx++) { + final ReentrantLock lock = MultiLockFairBlockingQueue.this.locks[idx]; + lock.lock(); + try { + boolean result = MultiLockFairBlockingQueue.this.items[idx].remove(elements[index]); + if (result) break; + } finally { + lock.unlock(); + } + } + + } + + } +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java new file mode 100644 index 0000000..42cf11d --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java @@ -0,0 +1,917 @@ +/*- + * ============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; + +import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorDefinition; + +/** + * A list of properties that are configurable for a connection pool. + * The {@link DataSource} object also implements this interface so that it can be easily configured through + * an IoC container without having to specify a secondary object with a setter method. + * + */ + +public interface PoolConfiguration { + + /** + * JMX prefix for interceptors that register themselves with JMX + */ + public static final String PKG_PREFIX = "org.apache.tomcat.jdbc.pool.interceptor."; + + /** + * Connections that have been abandoned (timed out) wont get closed and reported up unless the number of connections in use are + * above the percentage defined by abandonWhenPercentageFull. + * The value should be between 0-100. + * The default value is 0, which implies that connections are eligible for + * closure as soon as removeAbandonedTimeout has been reached. + * @param percentage a value between 0 and 100 to indicate when connections that have been abandoned/timed out are considered abandoned + */ + public void setAbandonWhenPercentageFull(int percentage); + + /** + * Connections that have been abandoned (timed out) wont get closed and reported up unless the number of connections in use are + * above the percentage defined by abandonWhenPercentageFull. + * The value should be between 0-100. + * The default value is 0, which implies that connections are eligible for + * closure as soon as removeAbandonedTimeout has been reached. + * @return percentage - a value between 0 and 100 to indicate when connections that have been abandoned/timed out are considered abandoned + */ + public int getAbandonWhenPercentageFull(); + + /** + * Returns <code>true</code> if a fair queue is being used by the connection pool + * @return <code>true</code> if a fair waiting queue is being used + */ + public boolean isFairQueue(); + + /** + * Set to true if you wish that calls to getConnection + * should be treated fairly in a true FIFO fashion. + * This uses the {@link FairBlockingQueue} implementation for the list of the idle connections. + * The default value is true. + * This flag is required when you want to use asynchronous connection retrieval. + * @param fairQueue <code>true</code> to use a fair queue + */ + public void setFairQueue(boolean fairQueue); + + /** + * Property not used. Access is always allowed. + * Access can be achieved by calling unwrap on the pooled connection. see {@link javax.sql.DataSource} interface + * or call getConnection through reflection or cast the object as {@link javax.sql.PooledConnection} + * @return <code>true</code> + */ + public boolean isAccessToUnderlyingConnectionAllowed(); + + /** + * No-op + * @param accessToUnderlyingConnectionAllowed parameter ignored + */ + public void setAccessToUnderlyingConnectionAllowed(boolean accessToUnderlyingConnectionAllowed); + + /** + * The connection properties that will be sent to the JDBC driver when establishing new connections. + * Format of the string is [propertyName=property;] <br> + * NOTE - The "user" and "password" properties will be passed explicitly, so they do not need to be included here. + * The default value is null. + * @return the connection properties + */ + public String getConnectionProperties(); + + /** + * The properties that will be passed into {@link java.sql.Driver#connect(String, Properties)} method. + * Username and password do not need to be stored here, they will be passed into the properties right before the connection is established. + * @param connectionProperties properties - Format of the string is [propertyName=property;]* + * Example: prop1=value1;prop2=value2 + */ + public void setConnectionProperties(String connectionProperties); + + /** + * Returns the database properties that are passed into the {@link java.sql.Driver#connect(String, Properties)} method. + * @return database properties that are passed into the {@link java.sql.Driver#connect(String, Properties)} method. + */ + public Properties getDbProperties(); + + /** + * Overrides the database properties passed into the {@link java.sql.Driver#connect(String, Properties)} method. + * @param dbProperties The database properties + */ + public void setDbProperties(Properties dbProperties); + + /** + * The default auto-commit state of connections created by this pool. + * If not set (null), default is JDBC driver default (If set to null then the {@link java.sql.Connection#setAutoCommit(boolean)} method will not be called.) + * @return the default auto commit setting, null is Driver default. + */ + public Boolean isDefaultAutoCommit(); + + /** + * The default auto-commit state of connections created by this pool. + * If not set (null), default is JDBC driver default (If set to null then the {@link java.sql.Connection#setAutoCommit(boolean)} method will not be called.) + * @return the default auto commit setting, null is Driver default. + */ + public Boolean getDefaultAutoCommit(); + + /** + * The default auto-commit state of connections created by this pool. + * If not set (null), default is JDBC driver default (If set to null then the {@link java.sql.Connection#setAutoCommit(boolean)} method will not be called.) + * @param defaultAutoCommit default auto commit setting, null is Driver default. + */ + public void setDefaultAutoCommit(Boolean defaultAutoCommit); + + /** + * If non null, during connection creation the method {@link java.sql.Connection#setCatalog(String)} will be called with the set value. + * @return the default catalog, null if not set and accepting the driver default. + */ + public String getDefaultCatalog(); + + /** + * If non null, during connection creation the method {@link java.sql.Connection#setCatalog(String)} will be called with the set value. + * @param defaultCatalog null if not set and accepting the driver default. + */ + public void setDefaultCatalog(String defaultCatalog); + + /** + * If non null, during connection creation the method {@link java.sql.Connection#setReadOnly(boolean)} will be called with the set value. + * @return null if not set and accepting the driver default otherwise the read only value + */ + public Boolean isDefaultReadOnly(); + + /** + * If non null, during connection creation the method {@link java.sql.Connection#setReadOnly(boolean)} will be called with the set value. + * @return null if not set and accepting the driver default otherwise the read only value + */ + public Boolean getDefaultReadOnly(); + + /** + * If non null, during connection creation the method {@link java.sql.Connection#setReadOnly(boolean)} will be called with the set value. + * @param defaultReadOnly null if not set and accepting the driver default. + */ + public void setDefaultReadOnly(Boolean defaultReadOnly); + + + /** + * Returns the default transaction isolation level. If set to {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION} the method + * {@link java.sql.Connection#setTransactionIsolation(int)} will not be called during connection creation. + * @return driver transaction isolation level, or -1 {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION} if not set. + */ + public int getDefaultTransactionIsolation(); + + /** + * If set to {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION} the method + * {@link java.sql.Connection#setTransactionIsolation(int)} will not be called during connection creation. Otherwise the method + * will be called with the isolation level set by this property. + * @param defaultTransactionIsolation a value of {@link java.sql.Connection#TRANSACTION_NONE}, {@link java.sql.Connection#TRANSACTION_READ_COMMITTED}, + * {@link java.sql.Connection#TRANSACTION_READ_UNCOMMITTED}, {@link java.sql.Connection#TRANSACTION_REPEATABLE_READ}, + * {@link java.sql.Connection#TRANSACTION_SERIALIZABLE} or {@link DataSourceFactory#UNKNOWN_TRANSACTIONISOLATION} + * The last value will not be set on the connection. + */ + public void setDefaultTransactionIsolation(int defaultTransactionIsolation); + + /** + * The fully qualified Java class name of the JDBC driver to be used. The driver has to be accessible from the same classloader as tomcat-jdbc.jar + * @return fully qualified JDBC driver name. + */ + public String getDriverClassName(); + + /** + * The fully qualified Java class name of the JDBC driver to be used. The driver has to be accessible from the same classloader as tomcat-jdbc.jar + * @param driverClassName a fully qualified Java class name of a {@link java.sql.Driver} implementation. + */ + public void setDriverClassName(String driverClassName); + + /** + * Returns the number of connections that will be established when the connection pool is started. + * Default value is 10 + * @return number of connections to be started when pool is started + */ + public int getInitialSize(); + + /** + * Set the number of connections that will be established when the connection pool is started. + * Default value is 10. + * If this value exceeds {@link #setMaxActive(int)} it will automatically be lowered. + * @param initialSize the number of connections to be established. + * + */ + public void setInitialSize(int initialSize); + + /** + * boolean flag to set if stack traces should be logged for application code which abandoned a Connection. + * Logging of abandoned Connections adds overhead for every Connection borrow because a stack trace has to be generated. + * The default value is false. + * @return true if the connection pool logs stack traces when connections are borrowed from the pool. + */ + public boolean isLogAbandoned(); + + /** + * boolean flag to set if stack traces should be logged for application code which abandoned a Connection. + * Logging of abandoned Connections adds overhead for every Connection borrow because a stack trace has to be generated. + * The default value is false. + * @param logAbandoned set to true if stack traces should be recorded when {@link DataSource#getConnection()} is called. + */ + public void setLogAbandoned(boolean logAbandoned); + + /** + * The maximum number of active connections that can be allocated from this pool at the same time. The default value is 100 + * @return the maximum number of connections used by this pool + */ + public int getMaxActive(); + + /** + * The maximum number of active connections that can be allocated from this pool at the same time. The default value is 100 + * @param maxActive hard limit for number of managed connections by this pool + */ + public void setMaxActive(int maxActive); + + + /** + * The maximum number of connections that should be kept in the idle pool if {@link #isPoolSweeperEnabled()} returns false. + * If the If {@link #isPoolSweeperEnabled()} returns true, then the idle pool can grow up to {@link #getMaxActive} + * and will be shrunk according to {@link #getMinEvictableIdleTimeMillis()} setting. + * Default value is maxActive:100 + * @return the maximum number of idle connections. + */ + public int getMaxIdle(); + + /** + * The maximum number of connections that should be kept in the idle pool if {@link #isPoolSweeperEnabled()} returns false. + * If the If {@link #isPoolSweeperEnabled()} returns true, then the idle pool can grow up to {@link #getMaxActive} + * and will be shrunk according to {@link #getMinEvictableIdleTimeMillis()} setting. + * Default value is maxActive:100 + * @param maxIdle the maximum size of the idle pool + */ + public void setMaxIdle(int maxIdle); + + /** + * The maximum number of milliseconds that the pool will wait (when there are no available connections and the + * {@link #getMaxActive} has been reached) for a connection to be returned + * before throwing an exception. Default value is 30000 (30 seconds) + * @return the number of milliseconds to wait for a connection to become available if the pool is maxed out. + */ + public int getMaxWait(); + + /** + * The maximum number of milliseconds that the pool will wait (when there are no available connections and the + * {@link #getMaxActive} has been reached) for a connection to be returned + * before throwing an exception. Default value is 30000 (30 seconds) + * @param maxWait the maximum number of milliseconds to wait. + */ + public void setMaxWait(int maxWait); + + /** + * The minimum amount of time an object must sit idle in the pool before it is eligible for eviction. + * The default value is 60000 (60 seconds). + * @return the minimum amount of idle time in milliseconds before a connection is considered idle and eligible for eviction. + */ + public int getMinEvictableIdleTimeMillis(); + + /** + * The minimum amount of time an object must sit idle in the pool before it is eligible for eviction. + * The default value is 60000 (60 seconds). + * @param minEvictableIdleTimeMillis the number of milliseconds a connection must be idle to be eligible for eviction. + */ + public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis); + + /** + * The minimum number of established connections that should be kept in the pool at all times. + * The connection pool can shrink below this number if validation queries fail and connections get closed. + * Default value is derived from {@link #getInitialSize()} (also see {@link #setTestWhileIdle(boolean)} + * The idle pool will not shrink below this value during an eviction run, hence the number of actual connections + * can be between {@link #getMinIdle()} and somewhere between {@link #getMaxIdle()} and {@link #getMaxActive()} + * @return the minimum number of idle or established connections + */ + public int getMinIdle(); + + /** + * The minimum number of established connections that should be kept in the pool at all times. + * The connection pool can shrink below this number if validation queries fail and connections get closed. + * Default value is derived from {@link #getInitialSize()} (also see {@link #setTestWhileIdle(boolean)} + * The idle pool will not shrink below this value during an eviction run, hence the number of actual connections + * can be between {@link #getMinIdle()} and somewhere between {@link #getMaxIdle()} and {@link #getMaxActive()} + * + * @param minIdle the minimum number of idle or established connections + */ + public void setMinIdle(int minIdle); + + /** + * Returns the name of the connection pool. By default a JVM unique random name is assigned. + * @return the name of the pool, should be unique in a JVM + */ + public String getName(); + + /** + * Sets the name of the connection pool + * @param name the name of the pool, should be unique in a runtime JVM + */ + public void setName(String name); + + /** + * Property not used + * @return unknown value + */ + public int getNumTestsPerEvictionRun(); + + /** + * Property not used + * @param numTestsPerEvictionRun parameter ignored. + */ + public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun); + + /** + * Returns the password used when establishing connections to the database. + * @return the password in string format + */ + public String getPassword(); + + /** + * Sets the password to establish the connection with. + * The password will be included as a database property with the name 'password'. + * @param password The password + * @see #getDbProperties() + */ + public void setPassword(String password); + + /** + * @see #getName() + * @return the pool name + */ + public String getPoolName(); + + /** + * Returns the username used to establish the connection with + * @return the username used to establish the connection with + */ + public String getUsername(); + + /** + * Sets the username used to establish the connection with + * It will also be a property called 'user' in the database properties. + * @param username The user name + * @see #getDbProperties() + */ + public void setUsername(String username); + + + /** + * boolean flag to remove abandoned connections if they exceed the removeAbandonedTimout. + * If set to true a connection is considered abandoned and eligible for removal if it has + * been in use longer than the {@link #getRemoveAbandonedTimeout()} and the condition for + * {@link #getAbandonWhenPercentageFull()} is met. + * Setting this to true can recover db connections from applications that fail to close a connection. + * See also {@link #isLogAbandoned()} The default value is false. + * @return true if abandoned connections can be closed and expelled out of the pool + */ + public boolean isRemoveAbandoned(); + + /** + * boolean flag to remove abandoned connections if they exceed the removeAbandonedTimout. + * If set to true a connection is considered abandoned and eligible for removal if it has + * been in use longer than the {@link #getRemoveAbandonedTimeout()} and the condition for + * {@link #getAbandonWhenPercentageFull()} is met. + * Setting this to true can recover db connections from applications that fail to close a connection. + * See also {@link #isLogAbandoned()} The default value is false. + * @param removeAbandoned set to true if abandoned connections can be closed and expelled out of the pool + */ + public void setRemoveAbandoned(boolean removeAbandoned); + + /** + * The time in seconds before a connection can be considered abandoned. + * The timer can be reset upon queries using an interceptor. + * @param removeAbandonedTimeout the time in seconds before a used connection can be considered abandoned + * @see org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer + */ + public void setRemoveAbandonedTimeout(int removeAbandonedTimeout); + + /** + * The time in seconds before a connection can be considered abandoned. + * The timer can be reset upon queries using an interceptor. + * @see org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer + * @return the time in seconds before a used connection can be considered abandoned + */ + public int getRemoveAbandonedTimeout(); + + /** + * The indication of whether objects will be validated before being borrowed from the pool. + * If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another. + * NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string. + * Default value is false + * In order to have a more efficient validation, see {@link #setValidationInterval(long)} + * @return true if the connection is to be validated upon borrowing a connection from the pool + * @see #getValidationInterval() + */ + public boolean isTestOnBorrow(); + + /** + * The indication of whether objects will be validated before being borrowed from the pool. + * If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another. + * NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string. + * Default value is false + * In order to have a more efficient validation, see {@link #setValidationInterval(long)} + * @param testOnBorrow set to true if validation should take place before a connection is handed out to the application + * @see #getValidationInterval() + */ + public void setTestOnBorrow(boolean testOnBorrow); + + /** + * The indication of whether objects will be validated after being returned to the pool. + * If the object fails to validate, it will be dropped from the pool. + * NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string. + * Default value is false + * In order to have a more efficient validation, see {@link #setValidationInterval(long)} + * @return true if validation should take place after a connection is returned to the pool + * @see #getValidationInterval() + */ + public boolean isTestOnReturn(); + + /** + * The indication of whether objects will be validated after being returned to the pool. + * If the object fails to validate, it will be dropped from the pool. + * NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string. + * Default value is false + * In order to have a more efficient validation, see {@link #setValidationInterval(long)} + * @param testOnReturn true if validation should take place after a connection is returned to the pool + * @see #getValidationInterval() + */ + public void setTestOnReturn(boolean testOnReturn); + + + /** + * Set to true if query validation should take place while the connection is idle. + * @return true if validation should take place during idle checks + * @see #setTimeBetweenEvictionRunsMillis(int) + */ + public boolean isTestWhileIdle(); + + /** + * Set to true if query validation should take place while the connection is idle. + * @param testWhileIdle true if validation should take place during idle checks + * @see #setTimeBetweenEvictionRunsMillis(int) + */ + public void setTestWhileIdle(boolean testWhileIdle); + + /** + * The number of milliseconds to sleep between runs of the idle connection validation, abandoned cleaner + * and idle pool resizing. This value should not be set under 1 second. + * It dictates how often we check for idle, abandoned connections, and how often we validate idle connection and resize the idle pool. + * The default value is 5000 (5 seconds) + * @return the sleep time in between validations in milliseconds + */ + public int getTimeBetweenEvictionRunsMillis(); + + /** + * The number of milliseconds to sleep between runs of the idle connection validation, abandoned cleaner + * and idle pool resizing. This value should not be set under 1 second. + * It dictates how often we check for idle, abandoned connections, and how often we validate idle connection and resize the idle pool. + * The default value is 5000 (5 seconds) + * @param timeBetweenEvictionRunsMillis the sleep time in between validations in milliseconds + */ + public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis); + + /** + * The URL used to connect to the database + * @return the configured URL for this connection pool + * @see java.sql.Driver#connect(String, Properties) + */ + public String getUrl(); + + /** + * Sets the URL used to connect to the database + * @param url the configured URL for this connection pool + * @see java.sql.Driver#connect(String, Properties) + */ + public void setUrl(String url); + + /** + * The SQL query that will be used to validate connections from this + * pool before returning them to the caller or pool. + * If specified, this query does not have to return any data, + * it just can't throw 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 + * <code> + boolean result = getTimeBetweenEvictionRunsMillis()>0; + result = result && (isRemoveAbandoned() && getRemoveAbandonedTimeout()>0); + result = result || (isTestWhileIdle() && getValidationQuery()!=null); + return result; + </code> + * + * @return true if a background thread is or will be enabled for this pool + */ + public boolean isPoolSweeperEnabled(); + + /** + * Set to true if you wish the <code>ProxyConnection</code> class to use <code>String.equals</code> instead of + * <code>==</code> when comparing method names. + * This property does not apply to added interceptors as those are configured individually. + * The default value is <code>false</code>. + * @return true if pool uses {@link String#equals(Object)} instead of == when comparing method names on {@link java.sql.Connection} methods + */ + public boolean isUseEquals(); + + /** + * Set to true if you wish the <code>ProxyConnection</code> class to use <code>String.equals</code> instead of + * <code>==</code> when comparing method names. + * This property does not apply to added interceptors as those are configured individually. + * The default value is <code>false</code>. + * @param useEquals set to true if the pool should use {@link String#equals(Object)} instead of == + * when comparing method names on {@link java.sql.Connection} methods + */ + public void setUseEquals(boolean useEquals); + + /** + * Time in milliseconds to keep this connection 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 <code>true</code> if the pool should commit when a connection is returned to it + */ + public boolean getCommitOnReturn(); + + /** + * Set to true if you want the connection pool to rollback any pending transaction when a connection is returned. + * The default value is false, as this could result in committing data. + * This parameter is only looked at if the {@link #getDefaultAutoCommit()} returns false + * @param rollbackOnReturn set to true if the pool should call {@link java.sql.Connection#rollback()} when a connection is returned to the pool. + * Default is false + */ + public void setRollbackOnReturn(boolean rollbackOnReturn); + + /** + * @see PoolConfiguration#setRollbackOnReturn(boolean) + * @return <code>true</code> if the pool should rollback when a connection is returned to it + */ + public boolean getRollbackOnReturn(); + + /** + * If set to <code>true</code>, the connection will be wrapped with facade that will disallow the connection to be used after + * {@link java.sql.Connection#close()} is called. If set to <code>true</code>, after {@link java.sql.Connection#close()} all calls except + * {@link java.sql.Connection#close()} and {@link java.sql.Connection#isClosed()} will throw an exception. + * @param useDisposableConnectionFacade <code>true</code> to use a facade + */ + public void setUseDisposableConnectionFacade(boolean useDisposableConnectionFacade); + /** + * Returns <code>true</code> if this connection pool is configured to use a connection facade to prevent re-use of connection after + * {@link java.sql.Connection#close()} has been invoked + * @return <code>true</code> if {@link java.sql.Connection#close()} has been invoked. + */ + public boolean getUseDisposableConnectionFacade(); + + /** + * Set to true if you wish that errors from validation should be logged as error messages. + * @param logValidationErrors set to true to log validation errors + */ + public void setLogValidationErrors(boolean logValidationErrors); + + /** + * Returns true if errors that happen during validation will be logged + * @return true if errors that happen during validation will be logged + */ + public boolean getLogValidationErrors(); + + /** + * Returns true if the pool is configured to propagate interrupt state of a thread. + * A thread waiting for a connection, can have its wait interrupted, and by default + * will clear the interrupt flag and throw a {@link PoolExhaustedException} + * @return true if the pool is configured to propagate and not clear the thread interrupt state + */ + public boolean getPropagateInterruptState(); + + /** + * Configure the pool to propagate interrupt state for interrupted threads waiting for a connection + * A thread waiting for a connection, can have its wait interrupted, and by default + * will clear the interrupt flag and throw a {@link PoolExhaustedException} + * If set to true, this behavior will change, while the {@link PoolExhaustedException} is still thrown, the threads interrupted state is still set. + * @param propagateInterruptState - set to true to not clear, but propagate, a threads interrupted state. + */ + public void setPropagateInterruptState(boolean propagateInterruptState); + + /** + * Set to true if you want to ignore error of connection creation while initializing the pool. + * Set to false if you want to fail the initialization of the pool by throwing exception. + * @param ignoreExceptionOnPreLoad set to true if you want to ignore error of connection creation while initializing the pool. + */ + public void setIgnoreExceptionOnPreLoad(boolean ignoreExceptionOnPreLoad); + + /** + * @return <code>true</code> to ignore exceptions + * @see PoolConfiguration#setIgnoreExceptionOnPreLoad(boolean) + */ + public boolean isIgnoreExceptionOnPreLoad(); + +} 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<interceptorValues.length; i++) { + int propIndex = interceptorValues[i].indexOf('('); + int endIndex = interceptorValues[i].indexOf(')'); + if (propIndex<0 || endIndex<0 || endIndex <= propIndex) { + definitions[i+1] = new InterceptorDefinition(interceptorValues[i].trim()); + } else { + String name = interceptorValues[i].substring(0,propIndex).trim(); + definitions[i+1] = new InterceptorDefinition(name); + String propsAsString = interceptorValues[i].substring(propIndex+1, endIndex); + String[] props = propsAsString.split(","); + for (int j=0; j<props.length; j++) { + int pidx = props[j].indexOf('='); + String propName = props[j].substring(0,pidx).trim(); + String propValue = props[j].substring(pidx+1).trim(); + definitions[i+1].addProperty(new InterceptorProperty(propName,propValue)); + } + } + } + interceptors = definitions; + } + } + return interceptors; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setAccessToUnderlyingConnectionAllowed(boolean accessToUnderlyingConnectionAllowed) { + // NOOP + } + + /** + * {@inheritDoc} + */ + + @Override + public void setConnectionProperties(String connectionProperties) { + this.connectionProperties = connectionProperties; + getProperties(connectionProperties, getDbProperties()); + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDbProperties(Properties dbProperties) { + this.dbProperties = dbProperties; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDefaultAutoCommit(Boolean defaultAutoCommit) { + this.defaultAutoCommit = defaultAutoCommit; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDefaultCatalog(String defaultCatalog) { + this.defaultCatalog = defaultCatalog; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDefaultReadOnly(Boolean defaultReadOnly) { + this.defaultReadOnly = defaultReadOnly; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDefaultTransactionIsolation(int defaultTransactionIsolation) { + this.defaultTransactionIsolation = defaultTransactionIsolation; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setDriverClassName(String driverClassName) { + this.driverClassName = driverClassName; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setInitialSize(int initialSize) { + this.initialSize = initialSize; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setLogAbandoned(boolean logAbandoned) { + this.logAbandoned = logAbandoned; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMaxActive(int maxActive) { + this.maxActive = maxActive; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMaxIdle(int maxIdle) { + this.maxIdle = maxIdle; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMaxWait(int maxWait) { + this.maxWait = maxWait; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) { + this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setMinIdle(int minIdle) { + this.minIdle = minIdle; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setName(String name) { + this.name = name; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { + this.numTestsPerEvictionRun = numTestsPerEvictionRun; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setPassword(String password) { + this.password = password; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setRemoveAbandoned(boolean removeAbandoned) { + this.removeAbandoned = removeAbandoned; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) { + this.removeAbandonedTimeout = removeAbandonedTimeout; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setTestOnBorrow(boolean testOnBorrow) { + this.testOnBorrow = testOnBorrow; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setTestWhileIdle(boolean testWhileIdle) { + this.testWhileIdle = testWhileIdle; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setTestOnReturn(boolean testOnReturn) { + this.testOnReturn = testOnReturn; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setTimeBetweenEvictionRunsMillis(int + timeBetweenEvictionRunsMillis) { + this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setUrl(String url) { + this.url = url; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setUsername(String username) { + this.username = username; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setValidationInterval(long validationInterval) { + this.validationInterval = validationInterval; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setValidationQuery(String validationQuery) { + this.validationQuery = validationQuery; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setValidatorClassName(String className) { + this.validatorClassName = className; + + validator = null; + + if (className == null) { + return; + } + + try { + @SuppressWarnings("unchecked") + Class<Validator> validatorClass = (Class<Validator>)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; j<prefix.length; j++) { + + String name = prefix[j] + + field.substring(0, 1).toUpperCase(Locale.ENGLISH) + + field.substring(1); + Method m = null; + try { + m = getClass().getMethod(name); + }catch (NoSuchMethodException nm) { + continue; + } + buf.append(field); + buf.append("="); + if (DataSourceFactory.PROP_PASSWORD.equals(field)) { + buf.append("********"); + } else { + buf.append(m.invoke(this, new Object[0])); + } + buf.append("; "); + break; + } + } + }catch (Exception x) { + //shouldn't happen + log.debug("toString() call failed", x); + } + return buf.toString(); + } + + public static int getPoolCounter() { + return poolCounter.get(); + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isJmxEnabled() { + return jmxEnabled; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setJmxEnabled(boolean jmxEnabled) { + this.jmxEnabled = jmxEnabled; + } + + /** + * {@inheritDoc} + */ + + @Override + public Boolean getDefaultAutoCommit() { + return defaultAutoCommit; + } + + /** + * {@inheritDoc} + */ + + @Override + public Boolean getDefaultReadOnly() { + return defaultReadOnly; + } + + + /** + * {@inheritDoc} + */ + + @Override + public int getSuspectTimeout() { + return this.suspectTimeout; + } + + /** + * {@inheritDoc} + */ + + @Override + public void setSuspectTimeout(int seconds) { + this.suspectTimeout = seconds; + } + + /** + * {@inheritDoc} + */ + + @Override + public boolean isPoolSweeperEnabled() { + boolean timer = getTimeBetweenEvictionRunsMillis()>0; + 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<String,InterceptorProperty> 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<String,InterceptorProperty> getProperties() { + return properties; + } + + @SuppressWarnings("unchecked") + public Class<? extends JdbcInterceptor> 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<? extends JdbcInterceptor>)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<Object, Object> 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 <code>true</code>if the pool does not need to reconnect + */ + @Deprecated + public boolean checkUser(String username, String password) { + return !shouldForceReconnect(username, password); + } + + /** + * Returns true if we must force reconnect based on credentials passed in. + * Returns false if {@link PoolConfiguration#isAlternateUsernameAllowed()} method returns false. + * Returns false if the username/password has not changed since this connection was connected + * @param username the username you wish to connect with, pass in null to accept the default username from {@link PoolConfiguration#getUsername()} + * @param password the password you wish to connect with, pass in null to accept the default username from {@link org.apache.tomcat.jdbc.pool.PoolConfiguration#getPassword()} + * @return true is the pool must reconnect + */ + public boolean shouldForceReconnect(String username, String password) { + + if (!getPoolProperties().isAlternateUsernameAllowed()) return false; + + if (username==null) username = poolProperties.getUsername(); + if (password==null) password = poolProperties.getPassword(); + + String storedUsr = (String)getAttributes().get(PROP_USER); + String storedPwd = (String)getAttributes().get(PROP_PASSWORD); + + boolean noChangeInCredentials = (username==null && storedUsr==null); + noChangeInCredentials = (noChangeInCredentials || (username!=null && username.equals(storedUsr))); + + noChangeInCredentials = noChangeInCredentials && ((password==null && storedPwd==null) || (password!=null && password.equals(storedPwd))); + + if (username==null) getAttributes().remove(PROP_USER); else getAttributes().put(PROP_USER, username); + if (password==null) getAttributes().remove(PROP_PASSWORD); else getAttributes().put(PROP_PASSWORD, password); + + return !noChangeInCredentials; + } + + /** + * Connects the underlying connection to the database. + * @throws SQLException if the method {@link #release()} has been called. + * @throws SQLException if driver instantiation fails + * @throws SQLException if a call to {@link java.sql.Driver#connect(String, java.util.Properties)} fails. + * @throws SQLException if default properties are configured and a call to + * {@link java.sql.Connection#setAutoCommit(boolean)}, {@link java.sql.Connection#setCatalog(String)}, + * {@link java.sql.Connection#setTransactionIsolation(int)} or {@link java.sql.Connection#setReadOnly(boolean)} fails. + */ + public void connect() throws SQLException { + if (released.get()) throw new SQLException("A connection once released, can't be reestablished."); + if (connection != null) { + try { + this.disconnect(false); + } catch (Exception x) { + log.debug("Unable to disconnect previous connection.", x); + } //catch + } //end if + if (poolProperties.getDataSource()==null && poolProperties.getDataSourceJNDI()!=null) { + //TODO lookup JNDI name + } + + if (poolProperties.getDataSource()!=null) { + connectUsingDataSource(); + } else { + connectUsingDriver(); + } + + //set up the default state, unless we expect the interceptor to do it + if (poolProperties.getJdbcInterceptors()==null || poolProperties.getJdbcInterceptors().indexOf(ConnectionState.class.getName())<0 || + poolProperties.getJdbcInterceptors().indexOf(ConnectionState.class.getSimpleName())<0) { + if (poolProperties.getDefaultTransactionIsolation()!=DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION) connection.setTransactionIsolation(poolProperties.getDefaultTransactionIsolation()); + if (poolProperties.getDefaultReadOnly()!=null) connection.setReadOnly(poolProperties.getDefaultReadOnly().booleanValue()); + if (poolProperties.getDefaultAutoCommit()!=null) connection.setAutoCommit(poolProperties.getDefaultAutoCommit().booleanValue()); + if (poolProperties.getDefaultCatalog()!=null) connection.setCatalog(poolProperties.getDefaultCatalog()); + } + this.discarded = false; + this.lastConnected = System.currentTimeMillis(); + } + + protected void connectUsingDataSource() throws SQLException { + String usr = null; + String pwd = null; + if (getAttributes().containsKey(PROP_USER)) { + usr = (String) getAttributes().get(PROP_USER); + } else { + usr = poolProperties.getUsername(); + getAttributes().put(PROP_USER, usr); + } + if (getAttributes().containsKey(PROP_PASSWORD)) { + pwd = (String) getAttributes().get(PROP_PASSWORD); + } else { + pwd = poolProperties.getPassword(); + getAttributes().put(PROP_PASSWORD, pwd); + } + if (poolProperties.getDataSource() instanceof javax.sql.XADataSource) { + javax.sql.XADataSource xds = (javax.sql.XADataSource)poolProperties.getDataSource(); + if (usr!=null && pwd!=null) { + xaConnection = xds.getXAConnection(usr, pwd); + connection = xaConnection.getConnection(); + } else { + xaConnection = xds.getXAConnection(); + connection = xaConnection.getConnection(); + } + } else if (poolProperties.getDataSource() instanceof javax.sql.DataSource){ + javax.sql.DataSource ds = (javax.sql.DataSource)poolProperties.getDataSource(); + if (usr!=null && pwd!=null) { + connection = ds.getConnection(usr, pwd); + } else { + connection = ds.getConnection(); + } + } else if (poolProperties.getDataSource() instanceof javax.sql.ConnectionPoolDataSource){ + javax.sql.ConnectionPoolDataSource ds = (javax.sql.ConnectionPoolDataSource)poolProperties.getDataSource(); + if (usr!=null && pwd!=null) { + connection = ds.getPooledConnection(usr, pwd).getConnection(); + } else { + connection = ds.getPooledConnection().getConnection(); + } + } else { + throw new SQLException("DataSource is of unknown class:"+(poolProperties.getDataSource()!=null?poolProperties.getDataSource().getClass():"null")); + } + } + protected void connectUsingDriver() throws SQLException { + + try { + 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 <code>true</code> if the connection pool is configured + * to do validation for a certain action. + * @param action The validation action + */ + private boolean doValidate(int action) { + if (action == PooledConnection.VALIDATE_BORROW && + poolProperties.isTestOnBorrow()) + return true; + else if (action == PooledConnection.VALIDATE_RETURN && + poolProperties.isTestOnReturn()) + return true; + else if (action == PooledConnection.VALIDATE_IDLE && + poolProperties.isTestWhileIdle()) + return true; + else if (action == PooledConnection.VALIDATE_INIT && + poolProperties.isTestOnConnect()) + return true; + else if (action == PooledConnection.VALIDATE_INIT && + poolProperties.getInitSQL()!=null) + return true; + else + return false; + } + + /** + * Returns <code>true</code> if the object is still valid. if not + * the pool will call the getExpiredAction() and follow up with one + * of the four expired methods + * @param validateAction The value + * @return <code>true</code> if the connection is valid + */ + public boolean validate(int validateAction) { + return validate(validateAction,null); + } + + /** + * Validates a connection. + * @param validateAction the action used. One of {@link #VALIDATE_BORROW}, {@link #VALIDATE_IDLE}, + * {@link #VALIDATE_INIT} or {@link #VALIDATE_RETURN} + * @param sql the SQL to be used during validation. If the {@link PoolConfiguration#setInitSQL(String)} has been called with a non null + * value and the action is {@link #VALIDATE_INIT} the init SQL will be used for validation. + * + * @return true if the connection was validated successfully. It returns true even if validation was not performed, such as when + * {@link PoolConfiguration#setValidationInterval(long)} has been called with a positive value. + * <p> + * false if the validation failed. The caller should close the connection if false is returned since a session could have been left in + * an unknown state during initialization. + */ + public boolean validate(int validateAction,String sql) { + if (this.isDiscarded()) { + return false; + } + + if (!doValidate(validateAction)) { + //no validation required, no init sql and props not set + return true; + } + + //Don't bother validating if already have recently enough + long now = System.currentTimeMillis(); + if (validateAction!=VALIDATE_INIT && + poolProperties.getValidationInterval() > 0 && + (now - this.lastValidated) < + poolProperties.getValidationInterval()) { + return true; + } + + if (poolProperties.getValidator() != null) { + if (poolProperties.getValidator().validate(connection, validateAction)) { + this.lastValidated = now; + return true; + } else { + if (getPoolProperties().getLogValidationErrors()) { + log.error("Custom validation through "+poolProperties.getValidator()+" failed."); + } + return false; + } + } + + String query = sql; + + if (validateAction == VALIDATE_INIT && poolProperties.getInitSQL() != null) { + query = poolProperties.getInitSQL(); + } + + if (query == null) { + query = poolProperties.getValidationQuery(); + } + + if (query == null) { + 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<Object,Object> 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: + * <ul> + * <li>{@link java.sql.Connection#close()} - returns the connection to the pool. May be called multiple times.</li> + * <li>{@link java.lang.Object#toString()} - returns a custom string for this object</li> + * <li>{@link javax.sql.PooledConnection#getConnection()} - returns the underlying connection</li> + * </ul> + * By default method comparisons is done on a String reference level, unless the {@link PoolConfiguration#setUseEquals(boolean)} has been called + * with a <code>true</code> argument. + */ +public class ProxyConnection extends JdbcInterceptor { + + protected PooledConnection connection = null; + + protected ConnectionPool pool = null; + + public PooledConnection getConnection() { + return connection; + } + + public void setConnection(PooledConnection connection) { + this.connection = connection; + } + + public ConnectionPool getPool() { + return pool; + } + + public void setPool(ConnectionPool pool) { + this.pool = pool; + } + + protected ProxyConnection(ConnectionPool parent, PooledConnection con, + boolean useEquals) { + pool = parent; + connection = con; + setUseEquals(useEquals); + } + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + this.pool = parent; + this.connection = con; + } + + public boolean isWrapperFor(Class<?> iface) { + if (iface == XAConnection.class && connection.getXAConnection()!=null) { + return true; + } else { + return (iface.isInstance(connection.getConnection())); + } + } + + + public Object unwrap(Class<?> iface) throws SQLException { + if (iface == PooledConnection.class) { + return connection; + }else if (iface == XAConnection.class) { + return connection.getXAConnection(); + } else if (isWrapperFor(iface)) { + return connection.getConnection(); + } else { + throw new SQLException("Not a wrapper of "+iface.getName()); + } + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (compare(ISCLOSED_VAL,method)) { + return Boolean.valueOf(isClosed()); + } + if (compare(CLOSE_VAL,method)) { + if (connection==null) return null; //noop for already closed. + PooledConnection poolc = this.connection; + this.connection = null; + pool.returnConnection(poolc); + return null; + } else if (compare(TOSTRING_VAL,method)) { + return this.toString(); + } else if (compare(GETCONNECTION_VAL,method) && connection!=null) { + return connection.getConnection(); + } else if (method.getDeclaringClass().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<names.length; i++) { + process = compare(names[i],name); + } + return process; + } + + /** + * no-op for this interceptor. no state is stored. + */ + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + // NOOP + } +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java new file mode 100644 index 0000000..374969d --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java @@ -0,0 +1,285 @@ +/*- + * ============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.SQLException; +import java.sql.Statement; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jdbc.pool.JdbcInterceptor; +/** + * Abstract class that wraps statements and intercepts query executions. + * + */ +public abstract class AbstractQueryReport extends AbstractCreateStatementInterceptor { + //logger + private static final Log log = LogFactory.getLog(AbstractQueryReport.class); + + /** + * The threshold in milliseconds. If the query is faster than this, we don't measure it + */ + protected long threshold = 1000; //don't report queries less than this + + /** + * the constructors that are used to create statement proxies + */ + protected static final Constructor<?>[] 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<readState.length; i++) { + read = compare(name,readState[i]); + if (read) index = i; + } + boolean write = false; + for (int i=0; (!write) && (!read) && i<writeState.length; i++) { + write = compare(name,writeState[i]); + if (write) index = i; + } + Object result = null; + if (read) { + switch (index) { + case 0:{result = autoCommit; break;} + case 1:{result = transactionIsolation; break;} + case 2:{result = readOnly; break;} + case 3:{result = catalog; break;} + default: // NOOP + } + //return cached result, if we have it + if (result!=null) return result; + } + + result = super.invoke(proxy, method, args); + if (read || write) { + switch (index) { + case 0:{autoCommit = (Boolean) (read?result:args[0]); break;} + case 1:{transactionIsolation = (Integer)(read?result:args[0]); break;} + case 2:{readOnly = (Boolean)(read?result:args[0]); break;} + case 3:{catalog = (String)(read?result:args[0]); break;} + } + } + return result; + } + +} diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/QueryTimeoutInterceptor.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/QueryTimeoutInterceptor.java new file mode 100644 index 0000000..7aec0b4 --- /dev/null +++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/QueryTimeoutInterceptor.java @@ -0,0 +1,77 @@ +/*- + * ============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 java.sql.Statement; +import java.util.Map; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty; + +public class QueryTimeoutInterceptor extends AbstractCreateStatementInterceptor { + private static Log log = LogFactory.getLog(QueryTimeoutInterceptor.class); + int timeout = 1; + + @Override + public void setProperties(Map<String,InterceptorProperty> properties) { + super.setProperties(properties); + InterceptorProperty p = properties.get("queryTimeout"); + if (p!=null) timeout = p.getValueAsInt(timeout); + } + + @Override + public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) { + if (statement instanceof Statement && timeout > 0) { + Statement s = (Statement)statement; + try { + s.setQueryTimeout(timeout); + }catch (SQLException x) { + log.warn("[QueryTimeoutInterceptor] Unable to set query timeout:"+x.getMessage(),x); + } + } + return statement; + } + + @Override + public void closeInvoked() { + } + +} 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<String,ConcurrentHashMap<String,QueryStats>> perPoolStats = + new ConcurrentHashMap<>(); + /** + * the queries that are used for this interceptor. + */ + protected volatile ConcurrentHashMap<String,QueryStats> queries = null; + /** + * Maximum number of queries we will be storing + */ + protected int maxQueries= 1000; //don't store more than this amount of queries + + /** + * Flag to enable disable logging of slow queries + */ + protected boolean logSlow = true; + + /** + * Flag to enable disable logging of failed queries + */ + protected boolean logFailed = false; + + /** + * Sort QueryStats by last invocation time + */ + protected final Comparator<QueryStats> queryStatsComparator = new QueryStatsComparator(); + + /** + * Returns the query stats for a given pool + * @param poolname - the name of the pool we want to retrieve stats for + * @return a hash map containing statistics for 0 to maxQueries + */ + public static ConcurrentHashMap<String,QueryStats> getPoolStats(String poolname) { + return perPoolStats.get(poolname); + } + + /** + * Creates a slow query report interceptor + */ + public SlowQueryReport() { + super(); + } + + public void setMaxQueries(int maxQueries) { + this.maxQueries = maxQueries; + } + + + @Override + protected String reportFailedQuery(String query, Object[] args, String name, long start, Throwable t) { + String sql = super.reportFailedQuery(query, args, name, start, t); + if (this.maxQueries > 0 ) { + long now = System.currentTimeMillis(); + long delta = now - start; + QueryStats qs = this.getQueryStats(sql); + if (qs != null) { + qs.failure(delta, now); + if (isLogFailed() && log.isWarnEnabled()) { + log.warn("Failed Query Report SQL="+sql+"; time="+delta+" ms;"); + } + } + } + return sql; + } + + @Override + protected String reportQuery(String query, Object[] args, final String name, long start, long delta) { + String sql = super.reportQuery(query, args, name, start, delta); + if (this.maxQueries > 0 ) { + QueryStats qs = this.getQueryStats(sql); + if (qs != null) qs.add(delta, start); + } + return sql; + } + + @Override + protected String reportSlowQuery(String query, Object[] args, String name, long start, long delta) { + String sql = super.reportSlowQuery(query, args, name, start, delta); + if (this.maxQueries > 0 ) { + QueryStats qs = this.getQueryStats(sql); + if (qs != null) { + qs.add(delta, start); + if (isLogSlow() && log.isWarnEnabled()) { + log.warn("Slow Query Report SQL="+sql+"; time="+delta+" ms;"); + } + } + } + return sql; + } + + /** + * invoked when the connection receives the close request + * Not used for now. + */ + @Override + public void closeInvoked() { + // NOOP + } + + @Override + public void prepareStatement(String sql, long time) { + if (this.maxQueries > 0 ) { + QueryStats qs = getQueryStats(sql); + if (qs != null) qs.prepare(time); + } + } + + @Override + public void prepareCall(String sql, long time) { + if (this.maxQueries > 0 ) { + QueryStats qs = getQueryStats(sql); + if (qs != null) qs.prepare(time); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void poolStarted(ConnectionPool pool) { + super.poolStarted(pool); + //see if we already created a map for this pool + queries = SlowQueryReport.perPoolStats.get(pool.getName()); + if (queries==null) { + //create the map to hold our stats + //however TODO we need to improve the eviction + //selection + queries = new ConcurrentHashMap<>(); + if (perPoolStats.putIfAbsent(pool.getName(), queries)!=null) { + //there already was one + queries = SlowQueryReport.perPoolStats.get(pool.getName()); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void poolClosed(ConnectionPool pool) { + perPoolStats.remove(pool.getName()); + super.poolClosed(pool); + } + + protected QueryStats getQueryStats(String sql) { + if (sql==null) sql = ""; + ConcurrentHashMap<String,QueryStats> queries = SlowQueryReport.this.queries; + if (queries==null) { + if (log.isWarnEnabled()) log.warn("Connection has already been closed or abandoned"); + return null; + } + QueryStats qs = queries.get(sql); + if (qs == null) { + qs = new QueryStats(sql); + if (queries.putIfAbsent(sql,qs)!=null) { + qs = queries.get(sql); + } else { + //we added a new element, see if we need to remove the oldest + if (queries.size() > maxQueries) { + removeOldest(queries); + } + } + } + return qs; + } + + /** + * Sort QueryStats by last invocation time + * @param queries The queries map + */ + protected void removeOldest(ConcurrentHashMap<String,QueryStats> queries) { + ArrayList<QueryStats> list = new ArrayList<>(queries.values()); + Collections.sort(list, queryStatsComparator); + int removeIndex = 0; + while (queries.size() > maxQueries) { + String sql = list.get(removeIndex).getQuery(); + queries.remove(sql); + if (log.isDebugEnabled()) log.debug("Removing slow query, capacity reached:"+sql); + removeIndex++; + } + } + + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + super.reset(parent, con); + if (parent!=null) + queries = SlowQueryReport.perPoolStats.get(parent.getName()); + else + queries = null; + } + + + public boolean isLogSlow() { + return logSlow; + } + + public void setLogSlow(boolean logSlow) { + this.logSlow = logSlow; + } + + public boolean isLogFailed() { + return logFailed; + } + + public void setLogFailed(boolean logFailed) { + this.logFailed = logFailed; + } + + @Override + public void setProperties(Map<String, InterceptorProperty> properties) { + super.setProperties(properties); + final String threshold = "threshold"; + final String maxqueries= "maxQueries"; + final String logslow = "logSlow"; + final String logfailed = "logFailed"; + InterceptorProperty p1 = properties.get(threshold); + InterceptorProperty p2 = properties.get(maxqueries); + InterceptorProperty p3 = properties.get(logslow); + InterceptorProperty p4 = properties.get(logfailed); + if (p1!=null) { + setThreshold(Long.parseLong(p1.getValue())); + } + if (p2!=null) { + setMaxQueries(Integer.parseInt(p2.getValue())); + } + if (p3!=null) { + setLogSlow(Boolean.parseBoolean(p3.getValue())); + } + if (p4!=null) { + setLogFailed(Boolean.parseBoolean(p4.getValue())); + } + } + + + public static class QueryStats { + static final String[] FIELD_NAMES = new String[] { + "query", + "nrOfInvocations", + "maxInvocationTime", + "maxInvocationDate", + "minInvocationTime", + "minInvocationDate", + "totalInvocationTime", + "failures", + "prepareCount", + "prepareTime", + "lastInvocation" + }; + + static final String[] FIELD_DESCRIPTIONS = new String[] { + "The SQL query", + "The number of query invocations, a call to executeXXX", + "The longest time for this query in milliseconds", + "The time and date for when the longest query took place", + "The shortest time for this query in milliseconds", + "The time and date for when the shortest query took place", + "The total amount of milliseconds spent executing this query", + "The number of failures for this query", + "The number of times this query was prepared (prepareStatement/prepareCall)", + "The total number of milliseconds spent preparing this query", + "The date and time of the last invocation" + }; + + static final OpenType<?>[] FIELD_TYPES = new OpenType[] { + SimpleType.STRING, + SimpleType.INTEGER, + SimpleType.LONG, + SimpleType.LONG, + SimpleType.LONG, + SimpleType.LONG, + SimpleType.LONG, + SimpleType.LONG, + SimpleType.INTEGER, + SimpleType.LONG, + SimpleType.LONG + }; + + private final String query; + private volatile int nrOfInvocations; + private volatile long maxInvocationTime = Long.MIN_VALUE; + private volatile long maxInvocationDate; + private volatile long minInvocationTime = Long.MAX_VALUE; + private volatile long minInvocationDate; + private volatile long totalInvocationTime; + private volatile long failures; + private volatile int prepareCount; + private volatile long prepareTime; + private volatile long lastInvocation = 0; + + public static String[] getFieldNames() { + return FIELD_NAMES; + } + + public static String[] getFieldDescriptions() { + return FIELD_DESCRIPTIONS; + } + + public static OpenType<?>[] getFieldTypes() { + return FIELD_TYPES; + } + + @Override + public String toString() { + SimpleDateFormat sdf = + new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US); + sdf.setTimeZone(TimeZone.getTimeZone("GMT")); + StringBuilder buf = new StringBuilder("QueryStats[query:"); + buf.append(query); + buf.append(", nrOfInvocations:"); + buf.append(nrOfInvocations); + buf.append(", maxInvocationTime:"); + buf.append(maxInvocationTime); + buf.append(", maxInvocationDate:"); + buf.append(sdf.format(new java.util.Date(maxInvocationDate))); + buf.append(", minInvocationTime:"); + buf.append(minInvocationTime); + buf.append(", minInvocationDate:"); + buf.append(sdf.format(new java.util.Date(minInvocationDate))); + buf.append(", totalInvocationTime:"); + buf.append(totalInvocationTime); + buf.append(", averageInvocationTime:"); + buf.append((float)totalInvocationTime / (float)nrOfInvocations); + buf.append(", failures:"); + buf.append(failures); + buf.append(", prepareCount:"); + buf.append(prepareCount); + buf.append(", prepareTime:"); + buf.append(prepareTime); + buf.append("]"); + return buf.toString(); + } + + public CompositeDataSupport getCompositeData(final CompositeType type) throws OpenDataException{ + Object[] values = new Object[] { + query, + Integer.valueOf(nrOfInvocations), + Long.valueOf(maxInvocationTime), + Long.valueOf(maxInvocationDate), + Long.valueOf(minInvocationTime), + Long.valueOf(minInvocationDate), + Long.valueOf(totalInvocationTime), + Long.valueOf(failures), + Integer.valueOf(prepareCount), + Long.valueOf(prepareTime), + Long.valueOf(lastInvocation) + }; + return new CompositeDataSupport(type,FIELD_NAMES,values); + } + + public QueryStats(String query) { + this.query = query; + } + + public void prepare(long invocationTime) { + prepareCount++; + prepareTime+=invocationTime; + + } + + public void add(long invocationTime, long now) { + //not thread safe, but don't sacrifice performance for this kind of stuff + maxInvocationTime = Math.max(invocationTime, maxInvocationTime); + if (maxInvocationTime == invocationTime) { + maxInvocationDate = now; + } + minInvocationTime = Math.min(invocationTime, minInvocationTime); + if (minInvocationTime==invocationTime) { + minInvocationDate = now; + } + nrOfInvocations++; + totalInvocationTime+=invocationTime; + lastInvocation = now; + } + + public void failure(long invocationTime, long now) { + add(invocationTime,now); + failures++; + + } + + public String getQuery() { + return query; + } + + public int getNrOfInvocations() { + return nrOfInvocations; + } + + public long getMaxInvocationTime() { + return maxInvocationTime; + } + + public long getMaxInvocationDate() { + return maxInvocationDate; + } + + public long getMinInvocationTime() { + return minInvocationTime; + } + + public long getMinInvocationDate() { + return minInvocationDate; + } + + public long getTotalInvocationTime() { + return totalInvocationTime; + } + + @Override + public int hashCode() { + return query.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof QueryStats) { + QueryStats qs = (QueryStats)other; + return qs.query.equals(this.query); + } + return false; + } + + public boolean isOlderThan(QueryStats other) { + return this.lastInvocation < other.lastInvocation; + } + } + + /** Compare QueryStats by their lastInvocation value. QueryStats that + * have never been updated, have a lastInvocation value of {@code 0} + * which should be handled as the newest possible invocation. + */ + private static class QueryStatsComparator implements Comparator<QueryStats> { + + @Override + public int compare(QueryStats stats1, QueryStats stats2) { + return Long.compare(handleZero(stats1.lastInvocation), + handleZero(stats2.lastInvocation)); + } + + private static long handleZero(long value) { + return value == 0 ? Long.MAX_VALUE : value; + } + + } + +} 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<String,SlowQueryReportJmxMBean> mbeans = + new ConcurrentHashMap<>(); + + + //==============================JMX STUFF======================== + protected volatile NotificationBroadcasterSupport notifier = new NotificationBroadcasterSupport(); + + @Override + public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException { + notifier.addNotificationListener(listener, filter, handback); + } + + + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + return notifier.getNotificationInfo(); + } + + @Override + public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { + notifier.removeNotificationListener(listener); + + } + + @Override + public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { + notifier.removeNotificationListener(listener, filter, handback); + + } + + + //==============================JMX STUFF======================== + + protected String poolName = null; + + protected static final AtomicLong notifySequence = new AtomicLong(0); + + protected boolean notifyPool = true; + + protected ConnectionPool pool = null; + + protected static CompositeType getCompositeType() { + if (SLOW_QUERY_TYPE==null) { + try { + SLOW_QUERY_TYPE = new CompositeType( + SlowQueryReportJmx.class.getName(), + "Composite data type for query statistics", + QueryStats.getFieldNames(), + QueryStats.getFieldDescriptions(), + QueryStats.getFieldTypes()); + }catch (OpenDataException x) { + log.warn("Unable to initialize composite data type for JMX stats and notifications.",x); + } + } + return SLOW_QUERY_TYPE; + } + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + super.reset(parent, con); + if (parent!=null) { + poolName = parent.getName(); + pool = parent; + registerJmx(); + } + } + + + @Override + public void poolClosed(ConnectionPool pool) { + this.poolName = pool.getName(); + deregisterJmx(); + super.poolClosed(pool); + } + + @Override + public void poolStarted(ConnectionPool pool) { + this.pool = pool; + super.poolStarted(pool); + this.poolName = pool.getName(); + } + + @Override + protected String reportFailedQuery(String query, Object[] args, String name, long start, Throwable t) { + query = super.reportFailedQuery(query, args, name, start, t); + if (isLogFailed()) notifyJmx(query,FAILED_QUERY_NOTIFICATION); + return query; + } + + protected void notifyJmx(String query, String type) { + try { + long sequence = notifySequence.incrementAndGet(); + + if (isNotifyPool()) { + if (this.pool!=null && this.pool.getJmxPool()!=null) { + this.pool.getJmxPool().notify(type, query); + } + } else { + if (notifier!=null) { + Notification notification = + new Notification(type, + this, + sequence, + System.currentTimeMillis(), + query); + + notifier.sendNotification(notification); + } + } + } catch (RuntimeOperationsException e) { + if (log.isDebugEnabled()) { + log.debug("Unable to send failed query notification.",e); + } + } + } + + @Override + protected String reportSlowQuery(String query, Object[] args, String name, long start, long delta) { + query = super.reportSlowQuery(query, args, name, start, delta); + if (isLogSlow()) notifyJmx(query,SLOW_QUERY_NOTIFICATION); + return query; + } + + /** + * JMX operation - return the names of all the pools + * @return - all the names of pools that we have stored data for + */ + public String[] getPoolNames() { + Set<String> keys = perPoolStats.keySet(); + return keys.toArray(new String[0]); + } + + /** + * JMX operation - return the name of the pool + * @return the name of the pool, unique within the JVM + */ + public String getPoolName() { + return poolName; + } + + + public boolean isNotifyPool() { + return notifyPool; + } + + public void setNotifyPool(boolean notifyPool) { + this.notifyPool = notifyPool; + } + + /** + * JMX operation - remove all stats for this connection pool + */ + public void resetStats() { + ConcurrentHashMap<String,QueryStats> queries = perPoolStats.get(poolName); + if (queries!=null) { + Iterator<String> it = queries.keySet().iterator(); + while (it.hasNext()) it.remove(); + } + } + + /** + * JMX operation - returns all the queries we have collected. + * @return - the slow query report as composite data. + */ + @Override + public CompositeData[] getSlowQueriesCD() throws OpenDataException { + CompositeDataSupport[] result = null; + ConcurrentHashMap<String,QueryStats> queries = perPoolStats.get(poolName); + if (queries!=null) { + Set<Map.Entry<String,QueryStats>> stats = queries.entrySet(); + if (stats!=null) { + result = new CompositeDataSupport[stats.size()]; + Iterator<Map.Entry<String,QueryStats>> it = stats.iterator(); + int pos = 0; + while (it.hasNext()) { + Map.Entry<String,QueryStats> entry = it.next(); + QueryStats qs = entry.getValue(); + result[pos++] = qs.getCompositeData(getCompositeType()); + } + } + } + return result; + } + + protected void deregisterJmx() { + try { + if (mbeans.remove(poolName)!=null) { + ObjectName oname = getObjectName(getClass(),poolName); + 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<String,InterceptorProperty> properties = getProperties(); + if (properties != null && properties.containsKey(objectNameAttribute)) { + oname = new ObjectName(properties.get(objectNameAttribute).getValue()); + } else { + oname = new ObjectName(ConnectionPool.POOL_JMX_TYPE_PREFIX+clazz.getName()+",name=" + poolName); + } + return oname; + } + + protected void registerJmx() { + try { + //only if we notify the pool itself + if (isNotifyPool()) { + + } else if (getCompositeType()!=null) { + ObjectName oname = getObjectName(getClass(),poolName); + if (mbeans.putIfAbsent(poolName, this)==null) { + 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<String, InterceptorProperty> 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<String, InterceptorProperty> properties) { + super.setProperties(properties); + InterceptorProperty p = properties.get("prepared"); + if (p!=null) cachePrepared = p.getValueAsBoolean(cachePrepared); + p = properties.get("callable"); + if (p!=null) cacheCallable = p.getValueAsBoolean(cacheCallable); + p = properties.get("max"); + if (p!=null) maxCacheSize = p.getValueAsInt(maxCacheSize); + if (cachePrepared && cacheCallable) { + this.types = ALL_TYPES; + } else if (cachePrepared) { + this.types = PREPARED_TYPE; + } else if (cacheCallable) { + this.types = CALLABLE_TYPE; + } else { + this.types = NO_TYPE; + } + + } + /*end properties for the statement cache*/ + + /*begin the cache size*/ + private static ConcurrentHashMap<ConnectionPool,AtomicInteger> cacheSizeMap = + new ConcurrentHashMap<>(); + + private AtomicInteger cacheSize; + + @Override + public void poolStarted(ConnectionPool pool) { + cacheSizeMap.putIfAbsent(pool, new AtomicInteger(0)); + super.poolStarted(pool); + } + + @Override + public void poolClosed(ConnectionPool pool) { + cacheSizeMap.remove(pool); + super.poolClosed(pool); + } + /*end the cache size*/ + + /*begin the actual statement cache*/ + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + super.reset(parent, con); + if (parent==null) { + cacheSize = null; + this.pcon = null; + } else { + cacheSize = cacheSizeMap.get(parent); + this.pcon = con; + if (!pcon.getAttributes().containsKey(STATEMENT_CACHE_ATTR)) { + ConcurrentHashMap<CacheKey,CachedStatement> cache = + new ConcurrentHashMap<>(); + pcon.getAttributes().put(STATEMENT_CACHE_ATTR,cache); + } + } + } + + @Override + public void disconnected(ConnectionPool parent, PooledConnection con, boolean finalizing) { + @SuppressWarnings("unchecked") + ConcurrentHashMap<CacheKey,CachedStatement> statements = + (ConcurrentHashMap<CacheKey,CachedStatement>)con.getAttributes().get(STATEMENT_CACHE_ATTR); + + if (statements!=null) { + for (Map.Entry<CacheKey, CachedStatement> p : statements.entrySet()) { + closeStatement(p.getValue()); + } + statements.clear(); + } + + super.disconnected(parent, con, finalizing); + } + + public void closeStatement(CachedStatement st) { + if (st==null) return; + st.forceClose(); + } + + @Override + protected Object createDecorator(Object proxy, Method method, Object[] args, + Object statement, Constructor<?> constructor, String sql) + throws InstantiationException, IllegalAccessException, InvocationTargetException { + boolean process = process(this.types, method, false); + if (process) { + Object result = null; + CachedStatement statementProxy = new CachedStatement((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<CacheKey,CachedStatement> cache = + (ConcurrentHashMap<CacheKey,CachedStatement>)pcon.getAttributes().get(STATEMENT_CACHE_ATTR); + return cache.get(createCacheKey(method, args)); + } + + public boolean cacheStatement(CachedStatement proxy) { + @SuppressWarnings("unchecked") + ConcurrentHashMap<CacheKey,CachedStatement> cache = + (ConcurrentHashMap<CacheKey,CachedStatement>)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<CacheKey,CachedStatement> cache = + (ConcurrentHashMap<CacheKey,CachedStatement>)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<Statement> { + 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 <b>JdbcInterceptor</b> that proxies resultSets and statements. + * @author Guillermo Fernandes + */ +public class StatementDecoratorInterceptor extends AbstractCreateStatementInterceptor { + + private static final Log logger = LogFactory.getLog(StatementDecoratorInterceptor.class); + + protected static final String EXECUTE_QUERY = "executeQuery"; + protected static final String GET_GENERATED_KEYS = "getGeneratedKeys"; + protected static final String GET_RESULTSET = "getResultSet"; + + protected static final String[] RESULTSET_TYPES = {EXECUTE_QUERY, GET_GENERATED_KEYS, GET_RESULTSET}; + + /** + * the 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<Statement> statementProxy = + new StatementProxy<>((Statement)statement,sql); + result = constructor.newInstance(new Object[] { statementProxy }); + statementProxy.setActualProxy(result); + statementProxy.setConnection(proxy); + statementProxy.setConstructor(constructor); + return result; + } + + protected boolean isExecuteQuery(String methodName) { + return EXECUTE_QUERY.equals(methodName); + } + + protected boolean isExecuteQuery(Method method) { + return isExecuteQuery(method.getName()); + } + + protected boolean isResultSet(Method method, boolean process) { + return process(RESULTSET_TYPES, method, process); + } + + /** + * Class to measure query execute time. + */ + protected class StatementProxy<T extends java.sql.Statement> implements InvocationHandler { + + protected boolean closed = false; + protected T delegate; + private Object actualProxy; + private Object connection; + private String sql; + private Constructor<?> constructor; + + public StatementProxy(T delegate, String sql) { + this.delegate = delegate; + this.sql = sql; + } + public T getDelegate() { + return this.delegate; + } + + public String getSql() { + return sql; + } + + public void setConnection(Object proxy) { + this.connection = proxy; + } + public Object getConnection() { + return this.connection; + } + + public void setActualProxy(Object proxy){ + this.actualProxy = proxy; + } + public Object getActualProxy() { + return this.actualProxy; + } + + + public Constructor<?> getConstructor() { + return constructor; + } + public void setConstructor(Constructor<?> constructor) { + this.constructor = constructor; + } + public void closeInvoked() { + if (getDelegate()!=null) { + try { + getDelegate().close(); + }catch (SQLException ignore) { + } + } + closed = true; + delegate = null; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (compare(TOSTRING_VAL,method)) { + return toString(); + } + // was close invoked? + boolean close = compare(CLOSE_VAL, method); + // allow close to be called multiple times + if (close && closed) + return null; + // are we calling isClosed? + if (compare(ISCLOSED_VAL, method)) + return Boolean.valueOf(closed); + // if we are calling anything else, bail out + if (closed) + throw new SQLException("Statement closed."); + if (compare(GETCONNECTION_VAL,method)){ + return connection; + } + boolean process = false; + process = isResultSet(method, process); + // check to see if we are about to execute a query + // if we are executing, get the current time + Object result = null; + try { + // perform close cleanup + if (close) { + closeInvoked(); + } else { + // execute the query + result = method.invoke(delegate, args); + } + } catch (Throwable t) { + if (t instanceof InvocationTargetException + && t.getCause() != null) { + throw t.getCause(); + } else { + throw t; + } + } + if (process && result != null) { + Constructor<?> cons = getResultSetConstructor(); + result = cons.newInstance(new Object[]{new ResultSetProxy(actualProxy, result)}); + } + return result; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(StatementProxy.class.getName()); + buf.append("[Proxy="); + buf.append(System.identityHashCode(this)); + buf.append("; Sql="); + buf.append(getSql()); + buf.append("; Delegate="); + buf.append(getDelegate()); + buf.append("; Connection="); + buf.append(getConnection()); + buf.append("]"); + return buf.toString(); + } + } + + protected class ResultSetProxy implements InvocationHandler { + + private Object st; + private Object delegate; + + public ResultSetProxy(Object st, Object delegate) { + this.st = st; + this.delegate = delegate; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("getStatement")) { + return this.st; + } else { + try { + return method.invoke(this.delegate, args); + } catch (Throwable t) { + if (t instanceof InvocationTargetException + && t.getCause() != null) { + throw t.getCause(); + } else { + throw t; + } + } + } + } + } +} 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<StatementEntry> statements = new LinkedList<>(); + + private boolean logCreationStack = false; + + @Override + public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) { + try { + if (statement instanceof Statement) + statements.add(new StatementEntry((Statement)statement)); + }catch (ClassCastException x) { + //ignore this one + } + return statement; + } + + @SuppressWarnings("null") // st is not null when used + @Override + public void closeInvoked() { + while (statements.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<String, PoolProperties.InterceptorProperty> properties) { + super.setProperties(properties); + + PoolProperties.InterceptorProperty logProperty = properties.get("trace"); + if (null != logProperty) { + logCreationStack = logProperty.getValueAsBoolean(logCreationStack); + } + } + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + statements.clear(); + super.reset(parent, con); + } + + protected class StatementEntry { + private WeakReference<Statement> statement; + private Throwable allocationStack; + + public StatementEntry(Statement statement) { + this.statement = new WeakReference<>(statement); + if (logCreationStack) { + this.allocationStack = new Throwable(); + } + } + + public Statement getStatement() { + return statement.get(); + } + + public Throwable getAllocationStack() { + return allocationStack; + } + } + + +} 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============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. +--> +<mbeans-descriptors> + + <mbean description="Reports " domain="tomcat.jdbc" group="jdbc-pool" name="SlowQueryReportJmx" + type="org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx"> + + <attribute description="The name of the connection pool this Jmx bean is representing" name="poolName" type="java.lang.String" writeable="false"/> + <attribute description="List of all registered connections pools" name="poolNames" type="[java.lang.String;" writeable="false"/> + <attribute description="All the recorded query stats. " name="slowQueriesCD" type="[javax.management.openmbean.CompositeData;" writeable="false"/> + <operation description="Clears all the query stats" impact="ACTION" name="resetStats" returnType="void"/> + + <notification description="Notification sent out by the slow query report when a query exceeds the threshold" name="slow-query"> + <notification-type>Slow query</notification-type> + </notification> + + <notification description="Notification sent out by the slow query report when a query fails execution" name="failed-query"> + <notification-type>Failed query execution</notification-type> + </notification> + </mbean> +</mbeans-descriptors> 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<NotificationListener> 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============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. +--> +<mbeans-descriptors> + + <mbean name="TomcatJDBCPool" + description="Provides per diagnostic metrics and notifications for JDBC operations" + domain="tomcat" + group="jdbc" + type="org.apache.tomcat.jdbc.pool.DataSource"> + + <attribute name="className" + description="Fully qualified class name of the managed object" + type="java.lang.String" + writeable="false"/> + + <attribute name="size" + description="The number of established connections in the pool, idle and in use" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="idle" + description="The number of established connections in the pool that are idle" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="numIdle" + description="Same as the idle attribute" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="active" + description="The number of established connections in the pool that are in use" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="numActive" + description="Same as the active attribute" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="poolSweeperEnabled" + description="Returns true if the pool has a background thread running" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="url" + description="The JDBC url for this connection pool" + type="java.lang.String" + writeable="false"/> + + <attribute name="driverClassName" + description="The JDBC driver class for this connection pool" + type="java.lang.String" + writeable="false"/> + + <attribute name="defaultAutoCommit" + description="The JDBC auto commit setting for new connections" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="defaultReadOnly" + description="The JDBC read only setting for new connections" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="defaultTransactionIsolation" + description="The JDBC transaction isolation setting for new connections" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="connectionProperties" + description="The connection properties that will be set for new connections. Format of the string will be [propertyName=property;]*" + type="java.lang.String" + writeable="false"/> + + <attribute name="defaultCatalog" + description="The JDBC transaction isolation setting for new connections" + type="java.lang.String" + writeable="false"/> + + <attribute name="initialSize" + description="The number of connections opened at pool startup" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="maxActive" + description="The maximum number of open connections" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="maxIdle" + description="The max number of idle connections" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="minIdle" + description="The minimum number of open connections" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="maxWait" + description="The time to wait in milliseconds before a SQLException is thrown when a connection is requested" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="validationQuery" + description="The query to run during validation" + type="java.lang.String" + writeable="false"/> + + <attribute name="validationQueryTimeout" + description="The timeout in seconds before a connection validation queries fail" + type="java.lang.Integer" + writeable="false" /> + + <attribute name="testOnBorrow" + description="True if validation happens when a connection is requested" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="testOnReturn" + description="True if validation happens when a connection is returned" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="testWhileIdle" + description="True if validation happens when a connection is not in use (idle)" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="timeBetweenEvictionRunsMillis" + description="Sleep time for background thread in between pool checks" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="numTestsPerEvictionRun" + description="Not in use" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="minEvictableIdleTimeMillis" + description="Minimum amount of time a connection stays idle before it is evicted" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="accessToUnderlyingConnectionAllowed" + description="Returns true if one can retrieve the actual JDBC connection" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="removeAbandoned" + description="Returns true if connection in use can be timed out" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="removeAbandonedTimeout" + description="Timeout in seconds for connections in use" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="logAbandoned" + description="If true, stack trace will be recorded and printed out for timed out connection" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="loginTimeout" + description="Not in use" + type="java.lang.Integer" + writeable="false"/> + + + <attribute name="name" + description="The name of the connection pool, will be used in the ObjectName of the actual pool" + type="java.lang.String" + writeable="false"/> + + <attribute name="password" + description="For security purposes,this doesn't return anything" + type="java.lang.String" + writeable="false"/> + + <attribute name="username" + description="The username used to open connections" + type="java.lang.String" + writeable="false"/> + + <attribute name="validationInterval" + description="If larger than zero than validation will only occur after the interval milliseconds has passed" + type="java.lang.Long" + writeable="false"/> + + <attribute name="initSQL" + description="A SQL executed once per connection, when it is established" + type="java.lang.String" + writeable="false"/> + + <attribute name="testOnConnect" + description="Validate connection after connection has been established" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="jdbcInterceptors" + description="The interceptors configured for this pool" + type="java.lang.String" + writeable="false"/> + + <attribute name="jmxEnabled" + description="Register the pool with JMX or not" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="fairQueue" + description="a fair queue is being used by the connection pool" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="abandonWhenPercentageFull" + description="Connections that have been abandoned isn't closed unless connections in use are above this percentage" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="maxAge" + description="Time in milliseconds to keep this connection alive even when used" + type="java.lang.Long" + writeable="false"/> + + <attribute name="useEquals" + description="Set to true if you wish the ProxyConnection class to use String.equals and set to false when you wish to use == when comparing method names" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="useLock" + description="If true, use a lock when operations are performed on the connection object" + type="java.lang.Boolean" + is="false" + writeable="false"/> + + <attribute name="suspectTimeout" + description="Timeout in seconds for connection that suspected to have been abandoned" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="rollbackOnReturn" + description="If autoCommit==false then the pool can terminate the transaction by calling rollback on the connection as it is returned to the pool" + type="java.lang.Boolean" + is="false" + writeable="false"/> + + <attribute name="commitOnReturn" + description="If autoCommit==false then the pool can complete the transaction by calling commit on the connection as it is returned to the pool" + type="java.lang.Boolean" + is="false" + writeable="false"/> + + <attribute name="alternateUsernameAllowed" + description="If true, getConnection(username,password) is allowed" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="dataSource" + description="Data source that is injected into the pool" + type="javax.sql.DataSource" + writeable="false"/> + + <attribute name="dataSourceJNDI" + description="The JNDI name for a data source to be looked up" + type="java.lang.String" + writeable="false"/> + + <attribute name="useDisposableConnectionFacade" + description="If true, connection pool is configured to use a connection facade to prevent re-use of connection after close() has been invoked" + type="java.lang.Boolean" + is="false" + writeable="false"/> + + <attribute name="logValidationErrors" + description="Log errors during the validation phase to the log file" + type="java.lang.Boolean" + is="false" + writeable="false"/> + + <attribute name="validatorClassName" + description="The name of validator class which implements org.apache.tomcat.jdbc.pool.Validator interface" + type="java.lang.String" + writeable="false"/> + + <attribute name="waitCount" + description="The number of threads waiting for a connection" + type="java.lang.Integer" + writeable="false"/> + + <attribute name="propagateInterruptState" + description="If true, propagate the interrupt state for a thread that has been interrupted" + type="java.lang.Boolean" + is="false" + writeable="false"/> + + <attribute name="ignoreExceptionOnPreLoad" + description="If true, ignore error of connection creation while initializing the pool" + type="java.lang.Boolean" + is="true" + writeable="false"/> + + <attribute name="borrowedCount" + description="The total number of connections borrowed from this pool" + type="java.lang.Long" + writeable="false"/> + + <attribute name="createdCount" + description="The total number of connections created by this pool" + type="java.lang.Long" + writeable="false"/> + + <attribute name="returnedCount" + description="The total number of connections returned to this pool" + type="java.lang.Long" + writeable="false"/> + + <attribute name="releasedCount" + description="The total number of connections released from this pool" + type="java.lang.Long" + writeable="false"/> + + <attribute name="reconnectedCount" + description="The total number of connections reconnected by this pool." + type="java.lang.Long" + writeable="false"/> + + <attribute name="removeAbandonedCount" + description="The total number of connections released by remove abandoned." + type="java.lang.Long" + writeable="false"/> + + <attribute name="releasedIdleCount" + description="The total number of connections released by eviction." + type="java.lang.Long" + writeable="false"/> + + <operation name="checkIdle" + description="forces a check of idle connections" + impact="ACTION" + returnType="void" /> + + <operation name="checkAbandoned" + description="forces a check of abandoned connections" + impact="ACTION" + returnType="void" /> + + <operation name="testIdle" + description="forces a validation of abandoned connections" + impact="ACTION" + returnType="void" /> + + <operation name="purge" + description="Purges all connections in the pool" + impact="ACTION" + returnType="void" /> + + <operation name="purgeOnReturn" + description="Purges connections when they are returned from the pool" + impact="ACTION" + returnType="void" /> + + <operation name="resetStats" + description="reset the statistics of this pool." + impact="ACTION" + returnType="void" /> + + </mbean> + +</mbeans-descriptors> 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 @@ <feature name='sdnc-dblib' description="sdnc-dblib" version='${project.version}'> <!-- Most applications will have a dependency on the ODL MD-SAL Broker --> <feature version="${odl.mdsal.version}">odl-mdsal-broker</feature> + <bundle>mvn:org.openecomp.sdnc.core/dblib-common/${project.version}</bundle> <bundle>mvn:org.openecomp.sdnc.core/dblib-provider/${project.version}</bundle> <bundle>mvn:mysql/mysql-connector-java/${mysql.connector.version}</bundle> </feature> 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 @@ <name>AT&T</name> </organization> <modules> + <module>common</module> <module>provider</module> <module>features</module> <module>installer</module> 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 @@ <artifactId>mysql-connector-java</artifactId> <version>${mysql.connector.version}</version> </dependency> + <dependency> + <groupId>org.openecomp.sdnc.core</groupId> + <artifactId>dblib-common</artifactId> + <version>${project.version}</version> + </dependency> </dependencies> <build> @@ -61,7 +66,10 @@ <Bundle-SymbolicName>org.openecomp.sdnc.sli.resource.dblib</Bundle-SymbolicName> <Bundle-Activator>org.openecomp.sdnc.sli.resource.dblib.DBLIBResourceActivator</Bundle-Activator> <Export-Package>org.openecomp.sdnc.sli.resource.dblib;version=${project.version}</Export-Package> - <Import-Package>org.openecomp.sdnc.sli.*,org.osgi.framework.*,org.slf4j.*,com.mysql.jdbc.*,javax.sql.*</Import-Package> + <!-- + <Import-Package>org.openecomp.sdnc.sli.*,org.osgi.framework.*,org.slf4j.*,com.mysql.jdbc.*,javax.sql.*,org.apache.tomcat.*</Import-Package> + --> + <Import-Package>*</Import-Package> <Embed-Transitive>true</Embed-Transitive> </instructions> </configuration> 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<CachedDataSource> broken = Collections.synchronizedSet(new HashSet<CachedDataSource>()); 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<String> 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<CachedDataSource> + public class DBInitTask implements Callable<CachedDataSource> { private BaseDBConfiguration config = null; private Set<DBInitTask> activeTasks; - - public DBInitTask(JndiConfiguration jndiConfig, Set<DBInitTask> tasks){ - this.config = jndiConfig; - this.activeTasks = tasks; - } public DBInitTask(JDBCConfiguration jdbcconfig, Set<DBInitTask> 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<Properties> propertySets = new ArrayList<Properties>(); @@ -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<DBInitTask> - { - - public MyFutureTask(Callable<CachedDataSource> 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<max; i++){ - if(!sourceName.equals(list[i].getJndiConnectionName())) - continue; - - JndiConfiguration config = list[i]; - CachedDataSource dataSource = CachedDataSourceFactory.createDataSource(config); - cachedDS[0] = dataSource; - } - return cachedDS; - } - - public CachedDataSource[] initDBResourceManager(DbConfigPool dbConfig, DBResourceManager manager) /* throws Exception */ { -// WSConfigManagement ws = WSConfigManagement.getInstance(); - - ExecutorService threadExecutor = Executors.newFixedThreadPool(2); - // here create the data sources objects - JndiConfiguration[] list = dbConfig.getJndiDbSourceArray(); - FutureTask<DBInitTask>[] futures = new MyFutureTask[list.length]; - final Set<DBInitTask> tasks = new HashSet<DBInitTask>(); - if(LOGGER.isDebugEnabled()) - LOGGER.debug("Creating datasources."); - for(int i=0, max=list.length; i<max; i++){ - JndiConfiguration config = list[i]; -// if(manager.getJndiContextFactoryStr()!=null && manager.getJndiContextFactoryStr().trim().length()>0){ -// 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<max; i++){ - threadExecutor.execute(futures[i]); - } - // the timeout param is set is seconds. - long timeout = ((dbConfig.getTimeout() <= 0) ? 60L : dbConfig.getTimeout()); - timeout *= 1000; - // the timeout param is set is seconds, hence it needs to be multiplied by 1000. - tasks.wait(timeout); - if(LOGGER.isDebugEnabled()) - LOGGER.debug("initDBResourceManager wait completed."); - } - } catch(Exception exc) { - LOGGER.error("Failed to initialize JndiCachedDataSource. Reason: ", exc); - } - - if(threadExecutor != null){ - try { - threadExecutor.shutdown(); - } catch(Exception exc){} - } - - CachedDataSource[] cachedDS = new CachedDataSource[futures.length]; - - boolean initialized = false; - for(int i=0; i<futures.length; i++){ - Object obj = null; - if(futures[i].isDone()){ - try { - obj = futures[i].get(); - if(obj instanceof CachedDataSource){ - cachedDS[i] = (CachedDataSource)obj; - initialized = true; - LOGGER.info("DataSource "+list[i].getJndiConnectionName()+" initialized successfully"); - } - } catch (InterruptedException exc) { - LOGGER.error("DataSource "+list[i].getJndiConnectionName()+" initialization failed", exc); - } catch (ExecutionException exc) { - LOGGER.error("DataSource "+list[i].getJndiConnectionName()+" initialization failed", exc); - } catch (Exception exc) { - LOGGER.error("DataSource "+list[i].getJndiConnectionName()+" initialization failed", exc); - } - } else { - try { - obj = futures[i].get(); - if(obj instanceof CachedDataSource){ - - LOGGER.error("DataSource "+((CachedDataSource)obj).getDbConnectionName()+" failed"); - } - } catch (Exception exc) { - LOGGER.error("DataSource "+list[i].getJndiConnectionName()+" initialization failed", exc); - } - } - } - - if(!initialized){ - new Error("Failed to initialize DB Library."); - } - return cachedDS; - } - - public static AbstractResourceManagerFactory createIntstance() { - return new JNDIDbResourceManagerFactory(); - } - -} diff --git a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jndi/JndiCachedDataSource.java b/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jndi/JndiCachedDataSource.java deleted file mode 100644 index 19fa8e9..0000000 --- a/dblib/provider/src/main/java/org/openecomp/sdnc/sli/resource/dblib/jndi/JndiCachedDataSource.java +++ /dev/null @@ -1,131 +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.SQLFeatureNotSupportedException; -import java.util.Properties; - -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; - -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.JndiConfiguration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xml.sax.SAXException; - -/** - * @version $Revision: 1.2 $ - * Change Log - * Author Date Comments - * ============== ======== ==================================================== - * Rich Tabedzki - */ -public class JndiCachedDataSource extends CachedDataSource -{ - private static Logger LOGGER = LoggerFactory.getLogger(JndiCachedDataSource.class); - /** - * @param alarmLog - * @param jdbcElem - * @throws SAXException - * @throws ScpTblUpdateError - */ - public JndiCachedDataSource(BaseDBConfiguration xmlElem) throws DBConfigException - { - super(xmlElem); - } - - protected void configure(BaseDBConfiguration xmlElem) throws DBConfigException { - JndiConfiguration jdbcConfig = (JndiConfiguration)xmlElem; - String jndiContextFactoryStr = jdbcConfig.getJndiContextFactory(); - String jndiURLStr = jdbcConfig.getJndiURL(); - String jndiSourceStr = jdbcConfig.getJndiSource(); - - if(jdbcConfig.getConnTimeout() > 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 @@ <artifactId>commons-codec</artifactId> <version>${commons.codec.version}</version> </dependency> - + <dependency> + <groupId>org.openecomp.sdnc.core</groupId> + <artifactId>sli-common</artifactId> + <version>${sdnctl.sli.version}</version> + </dependency> <!-- <dependency> <groupId>mysql</groupId> diff --git a/filters/provider/src/main/java/org/openecomp/sdnc/filters/LogFilter.java b/filters/provider/src/main/java/org/openecomp/sdnc/filters/LogFilter.java index 40100c3..809cfb2 100644 --- a/filters/provider/src/main/java/org/openecomp/sdnc/filters/LogFilter.java +++ b/filters/provider/src/main/java/org/openecomp/sdnc/filters/LogFilter.java @@ -47,7 +47,7 @@ import java.util.TimeZone; /** - * Logs IN request according ECOMP Logging Guidelines + * Logs IN request according ECOMP Logging Guidelines at https://tspace.web.att.com/viewer/app/lcfiles/ae5f7751-39da-4c6b-8a83-5836c8c815e1/content */ public class LogFilter implements Filter { diff --git a/filters/provider/src/main/java/org/openecomp/sdnc/filters/RequestResponseDbLoggingFilter.java b/filters/provider/src/main/java/org/openecomp/sdnc/filters/RequestResponseDbLoggingFilter.java new file mode 100644 index 0000000..183301b --- /dev/null +++ b/filters/provider/src/main/java/org/openecomp/sdnc/filters/RequestResponseDbLoggingFilter.java @@ -0,0 +1,297 @@ +/*- + * ============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.filters; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.util.zip.GZIPInputStream; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.openecomp.sdnc.sli.MessageWriter; + +public class RequestResponseDbLoggingFilter implements Filter { + + private static Logger log = LoggerFactory.getLogger(RequestResponseDbLoggingFilter.class); + + public static final String REQUEST_ID = "X-ECOMP-RequestID"; + + private static class ByteArrayServletStream extends ServletOutputStream { + + ByteArrayOutputStream baos; + + ByteArrayServletStream(ByteArrayOutputStream baos) { + this.baos = baos; + } + + @Override + public void write(int param) throws IOException { + baos.write(param); + } + } + + private static class ByteArrayPrintWriter { + + private ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + private PrintWriter pw = new PrintWriter(baos); + + private ServletOutputStream sos = new ByteArrayServletStream(baos); + + public PrintWriter getWriter() { + return pw; + } + + public ServletOutputStream getStream() { + return sos; + } + + byte[] toByteArray() { + return baos.toByteArray(); + } + } + + private class BufferedServletInputStream extends ServletInputStream { + + ByteArrayInputStream bais; + + public BufferedServletInputStream(ByteArrayInputStream bais) { + this.bais = bais; + } + + @Override + public int available() { + return bais.available(); + } + + @Override + public int read() { + return bais.read(); + } + + @Override + public int read(byte[] buf, int off, int len) { + return bais.read(buf, off, len); + } + + } + + private class BufferedRequestWrapper extends HttpServletRequestWrapper { + + ByteArrayInputStream bais; + + ByteArrayOutputStream baos; + + BufferedServletInputStream bsis; + + byte[] buffer; + + public BufferedRequestWrapper(HttpServletRequest req) throws IOException { + super(req); + + InputStream is = req.getInputStream(); + baos = new ByteArrayOutputStream(); + byte buf[] = new byte[1024]; + int letti; + while ((letti = is.read(buf)) > 0) { + baos.write(buf, 0, letti); + } + buffer = baos.toByteArray(); + + } + + @Override + public ServletInputStream getInputStream() { + try { + bais = new ByteArrayInputStream(buffer); + bsis = new BufferedServletInputStream(bais); + } catch (Exception ex) { + ex.printStackTrace(); + } + + return bsis; + } + + public byte[] getBuffer() { + return buffer; + } + + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) + throws IOException, ServletException { + + if (request == null || !(request instanceof HttpServletRequest)) { + filterChain.doFilter(request, response); + return; + } + + long t1 = System.currentTimeMillis(); + + final HttpServletRequest httpRequest = (HttpServletRequest) request; + BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(httpRequest); + + String requestId = httpRequest.getHeader(REQUEST_ID); + if (requestId == null || requestId.trim().length() == 0) { + log.warn("Could not write request in DB: " + REQUEST_ID + " is missing in the HTTP headers."); + return; + } + + String requestHost = request.getRemoteHost(); + if (requestHost == null) + requestHost = request.getRemoteAddr(); + + String requestStr = new String(bufferedRequest.getBuffer()); + + MessageWriter.saveIncomingRequest(requestId, null, requestHost, requestStr); + + long t2 = System.currentTimeMillis(); + + log.info("Request saved in DB for request-id: " + requestId + ". TIme: " + (t2 - t1)); + + final HttpServletResponse httpResponse = (HttpServletResponse) response; + + final ByteArrayPrintWriter pw = new ByteArrayPrintWriter(); + HttpServletResponse wrappedResp = new HttpServletResponseWrapper(httpResponse) { + + @Override + public PrintWriter getWriter() { + return pw.getWriter(); + } + + @Override + public ServletOutputStream getOutputStream() { + return pw.getStream(); + } + }; + + try { + + filterChain.doFilter(bufferedRequest, wrappedResp); + + } finally { + + if (request != null && request instanceof HttpServletRequest) { + + t1 = System.currentTimeMillis(); + + byte[] bytes = pw.toByteArray(); + response.getOutputStream().write(bytes); + response.getOutputStream().flush(); + + String responseStr = null; + if ("gzip".equals(httpResponse.getHeader("Content-Encoding"))) { + responseStr = decompressGZIPByteArray(bytes); + } else { + responseStr = new String(bytes); + } + + MessageWriter.saveIncomingResponse(requestId, httpResponse.getStatus(), responseStr); + + t2 = System.currentTimeMillis(); + + log.info("Response saved in DB for request-id: " + requestId + ". TIme: " + (t2 - t1)); + } + } + + } + + @Override + public void destroy() { + } + + private String decompressGZIPByteArray(byte[] bytes) { + + BufferedReader in = null; + InputStreamReader inR = null; + ByteArrayInputStream byteS = null; + GZIPInputStream gzS = null; + StringBuilder str = new StringBuilder(); + try { + byteS = new ByteArrayInputStream(bytes); + gzS = new GZIPInputStream(byteS); + inR = new InputStreamReader(gzS); + in = new BufferedReader(inR); + + if (in != null) { + + String content; + + while ((content = in.readLine()) != null) { + str.append(content); + } + } + + } catch (Exception e) { + log.error("Failed get read GZIPInputStream", e); + } finally { + + if (byteS != null) + try { + byteS.close(); + } catch (IOException e1) { + log.error("Failed to close ByteStream", e1); + } + if (gzS != null) + try { + gzS.close(); + } catch (IOException e2) { + log.error("Failed to close GZStream", e2); + } + if (inR != null) + try { + inR.close(); + } catch (IOException e3) { + log.error("Failed to close InputReader", e3); + } + if (in != null) + try { + in.close(); + } catch (IOException e) { + log.error("Failed to close BufferedReader", e); + } + } + return str.toString(); + } +} diff --git a/filters/provider/src/main/java/org/openecomp/sdnc/filters/RequestResponseLoggingFilter.java b/filters/provider/src/main/java/org/openecomp/sdnc/filters/RequestResponseLoggingFilter.java index 48f0689..b15932f 100644 --- a/filters/provider/src/main/java/org/openecomp/sdnc/filters/RequestResponseLoggingFilter.java +++ b/filters/provider/src/main/java/org/openecomp/sdnc/filters/RequestResponseLoggingFilter.java @@ -30,7 +30,6 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.Enumeration; import java.util.zip.GZIPInputStream; -import java.util.zip.Inflater; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -57,6 +56,7 @@ public class RequestResponseLoggingFilter implements Filter { this.baos = baos; } + @Override public void write(int param) throws IOException { baos.write(param); } @@ -91,14 +91,17 @@ public class RequestResponseLoggingFilter implements Filter { this.bais = bais; } + @Override public int available() { return bais.available(); } + @Override public int read() { return bais.read(); } + @Override public int read(byte[] buf, int off, int len) { return bais.read(buf, off, len); } @@ -129,6 +132,7 @@ public class RequestResponseLoggingFilter implements Filter { } + @Override public ServletInputStream getInputStream() { try { bais = new ByteArrayInputStream(buffer); @@ -146,9 +150,11 @@ public class RequestResponseLoggingFilter implements Filter { } + @Override public void init(FilterConfig filterConfig) throws ServletException { } + @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { @@ -177,10 +183,12 @@ public class RequestResponseLoggingFilter implements Filter { final ByteArrayPrintWriter pw = new ByteArrayPrintWriter(); HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response) { + @Override public PrintWriter getWriter() { return pw.getWriter(); } + @Override public ServletOutputStream getOutputStream() { return pw.getStream(); } @@ -188,15 +196,16 @@ public class RequestResponseLoggingFilter implements Filter { }; try { - + filterChain.doFilter(bufferedRequest, wrappedResp); - + }catch (Exception e){ log.error("Chain Exception",e); throw e; } finally { byte[] bytes = pw.toByteArray(); response.getOutputStream().write(bytes); + response.getOutputStream().flush(); StringBuilder responseHeaders = new StringBuilder("RESPONSE HEADERS|"); @@ -220,6 +229,7 @@ public class RequestResponseLoggingFilter implements Filter { } } + @Override public void destroy() { } @@ -7,8 +7,9 @@ <groupId>org.openecomp.sdnc.core</groupId> <artifactId>sdnc-core</artifactId> + <name>SDN-C Core Components</name> - <url>https://wiki.onap.org</url> + <url>https://wiki.openecomp.org</url> <description>The SDN-C core components contains the SLI, dblib and root pom</description> <parent> @@ -21,7 +22,7 @@ <issueManagement> <system>JIRA</system> - <url>http://jira.onap.org/</url> + <url>http://jira.openecomp.org/</url> </issueManagement> @@ -34,7 +35,7 @@ <ciManagement> <system>Jenkins</system> - <url>https://jenkins.onap.org/</url> + <url>https://jenkins.openecomp.org/</url> </ciManagement> @@ -55,7 +56,6 @@ </property> </activation> <build> - <pluginManagement> <plugins> <plugin> <groupId>com.blackducksoftware.integration</groupId> @@ -78,7 +78,7 @@ </plugin> </plugins> - </pluginManagement> + </build> @@ -172,7 +172,6 @@ </build> <modules> - <module>rootpom</module> <module>dblib</module> <module>filters</module> <module>sli</module> diff --git a/rootpom/pom.xml b/rootpom/pom.xml index 7a59501..4938811 100755 --- a/rootpom/pom.xml +++ b/rootpom/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>org.opendaylight.odlparent</groupId> <artifactId>odlparent</artifactId> - <version>1.6.2-Beryllium-SR2</version> + <version>1.7.1-Boron-SR1</version> <relativePath>../../../../../opendaylight/odlparent</relativePath> </parent> @@ -28,37 +28,13 @@ <opendaylight.nexus.public-url>https://nexus.opendaylight.org/content/repositories/public/</opendaylight.nexus.public-url> <opendaylight.nexus.snapshot-url>https://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/</opendaylight.nexus.snapshot-url> - <release-tag>R17.01</release-tag> + <release-tag>R17.07</release-tag> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <java.version.source>1.8</java.version.source> + <java.version.target>1.8</java.version.target> <bundle.plugin.version>2.5.0</bundle.plugin.version> - <java.version.source>1.7</java.version.source> - <java.version.target>1.7</java.version.target> <maven.compile.plugin.version>2.5.1</maven.compile.plugin.version> - <equinox.osgi.version>3.8.1.v20120830-144521</equinox.osgi.version> - <jackson-annotations-version>${jackson.version}</jackson-annotations-version> - <jettison.version>1.3.7</jettison.version> - <jvnet.jaxb2.version>0.6.4</jvnet.jaxb2.version> - <apache.httpcomponents.version>4.4</apache.httpcomponents.version> - <antlr.version>4.5.1</antlr.version> - <mysql.connector.version>5.1.39</mysql.connector.version> - <odl.version>1.6.2-Beryllium-SR2</odl.version> - <odl.dlux.version>0.3.2-Beryllium-SR2</odl.dlux.version> - <odl.yangtools.version>0.8.2-Beryllium-SR2</odl.yangtools.version> - <odl.mdsal.version>1.3.2-Beryllium-SR2</odl.mdsal.version> - <odl.mdsal.features.version>2.0.2-Beryllium-SR2</odl.mdsal.features.version> - <odl.mdsal.model.version>0.8.2-Beryllium-SR2</odl.mdsal.model.version> - <odl.restconf.version>1.3.2-Beryllium-SR2</odl.restconf.version> - <odl.yangtools.version>0.8.2-Beryllium-SR2</odl.yangtools.version> - <odl.controller.model.version>${odl.mdsal.model.version}</odl.controller.model.version> - <odl.controller.config.api.version>0.4.2-Beryllium-SR2</odl.controller.config.api.version> - <odl.karaf.empty.distro.version>${odl.version}</odl.karaf.empty.distro.version> - <odl.commons.opendaylight.version>${odl.version}</odl.commons.opendaylight.version> - <odl.ietf-inet-types.version>2010.09.24.8.2-Beryllium-SR2</odl.ietf-inet-types.version> - <odl.ietf-yang-types.version>2010.09.24.8.2-Beryllium-SR2</odl.ietf-yang-types.version> - <odl.yang.jmx.generator.version>0.4.2-Beryllium-SR2</odl.yang.jmx.generator.version> - <odl.yangtools.yang.maven.plugin.version>${odl.yangtools.version}</odl.yangtools.yang.maven.plugin.version> <features.file>features.xml</features.file> - <h2database.version>1.4.186</h2database.version> <jmxGeneratorPath>src/main/yang-gen-config</jmxGeneratorPath> <salGeneratorPath>src/main/yang-gen-sal</salGeneratorPath> <checkstyle.skip>true</checkstyle.skip> @@ -70,13 +46,110 @@ <sonar.jacoco.reportMissing.force.zero>true</sonar.jacoco.reportMissing.force.zero> <sonar.projectVersion>${project.version}</sonar.projectVersion> + <!-- OpenDaylight component versions --> + <odl.version>1.7.1-Boron-SR1</odl.version> + <odl.dlux.version>0.4.1-Boron-SR1</odl.dlux.version> + <odl.yangtools.version>1.0.1-Boron-SR1</odl.yangtools.version> + <odl.mdsal.version>1.4.1-Boron-SR1</odl.mdsal.version> + <odl.mdsal.features.version>2.1.1-Boron-SR1</odl.mdsal.features.version> + <odl.mdsal.model.version>0.9.1-Boron-SR1</odl.mdsal.model.version> + <odl.mdsal.yang.binding.version>0.9.1-Boron-SR1</odl.mdsal.yang.binding.version> + <odl.restconf.version>1.4.1-Boron-SR1</odl.restconf.version> + <odl.controller.model.version>${odl.mdsal.model.version}</odl.controller.model.version> + <odl.controller.config.api.version>0.5.1-Boron-SR1</odl.controller.config.api.version> + <odl.karaf.empty.distro.version>${odl.version}</odl.karaf.empty.distro.version> + <odl.commons.opendaylight.version>${odl.version}</odl.commons.opendaylight.version> + <odl.ietf-inet-types.version>2010.09.24.9.1-Boron-SR1</odl.ietf-inet-types.version> + <odl.ietf-yang-types.version>2010.09.24.9.1-Boron-SR1</odl.ietf-yang-types.version> + <odl.yang.jmx.generator.version>0.5.1-Boron-SR1</odl.yang.jmx.generator.version> + <odl.yangtools.yang.maven.plugin.version>${odl.yangtools.version}</odl.yangtools.yang.maven.plugin.version> + <odl.sal.api.gen.plugin.version>0.9.1-Boron-SR1</odl.sal.api.gen.plugin.version> + + + + <!-- SDN-C component versions --> <sdnc.core.version>1.1.0-SNAPSHOT</sdnc.core.version> <sdnc.adaptors.version>1.1.0-SNAPSHOT</sdnc.adaptors.version> + <sdnc.northbound.version>1.1.0-SNAPSHOT</sdnc.northbound.version> + <sdnc.oam.version>1.1.0-SNAPSHOT</sdnc.oam.version> + <sdnc.plugins.version>1.1.0-SNAPSHOT</sdnc.plugins.version> <sdnctl.sli.version>${sdnc.core.version}</sdnctl.sli.version> <sdnctl.aai.service.version>${sdnc.adaptors.version}</sdnctl.aai.service.version> <sdnctl.dblib.version>${sdnc.core.version}</sdnctl.dblib.version> <sdnctl.mdsal.resource.version>${sdnc.adaptors.version}</sdnctl.mdsal.resource.version> <sdnctl.slipluginutils.version>${sdnc.core.version}</sdnctl.slipluginutils.version> + + <!-- Support libraries used by OpenDaylight --> + <!-- Used by aaa, vtn --> + <commons.codec.version>1.10</commons.codec.version> + <!-- Used by netconf, ovsdb --> + <commons.lang3.version>3.4</commons.lang3.version> + <!-- Used by sfc, snmp4sdn; see also affinity, toolkit --> + <commons.lang.version>2.6</commons.lang.version> + <!-- Used by neutron; see also controller, vtn --> + <commons.net.version>3.5</commons.net.version> + <!-- Used by neutron --> + <eclipse.persistence.version>2.6.2</eclipse.persistence.version> + <!-- Used by aaa --> + <felix.dependencymanager.version>4.3.0</felix.dependencymanager.version> + <!-- Used by lacp, netconf, ovsdb, sfc, toolkit; see also vtn --> + <gson.version>2.3.1</gson.version> + <!-- Used by aaa, dlux, persistence, snmp4sdn, sxp --> + <guava.version>18.0</guava.version> + <!-- Used by lispflowmapping, sxp, vtn/manager --> + <hamcrest.version>1.3</hamcrest.version> + <!-- Used by aaa, alto, netconf, ovsdb, sfc; see also affinity, defense4all, + integration/distribution, snmp4sdn, toolkit, ttp --> + <jackson.version>2.3.2</jackson.version> + <!-- Used by snmp4sdn, yangtools --> + <javassist.version>3.20.0-GA</javassist.version> + + <!-- FIXME remove all dependencies for jersey 1.17 after migration all + projects --> + <!-- Used by aaa, alto, netconf, neutron, sfc; see also affinity, defense4all, + snmp4sdn, toolkit --> + <jersey.version>1.17</jersey.version> + <!-- Used by sfc --> + <jersey.client.version>1.17</jersey.client.version> + + <!-- New packages for jersey migration 1.17 to 2.8 --> + <!-- appache.geronimo.bundle for DocGen --> + <!-- Used by aaa, iotdm, netconf, ovsdb, sfc --> + <org.json.version>20131018</org.json.version> + + <!-- Used by sfc --> + <jettison.version>1.3.7</jettison.version> + <!-- Used by persistence --> + <jsr305.api.version>3.0.0</jsr305.api.version> + <!-- Need to stick to JUnit 4.11 until https://github.com/jayway/powermock/issues/560 + is fixed (either in PowerMock or with a new JUnit release) --> + <!-- Used everywhere --> + <junit.version>4.11</junit.version> + <!-- Used by coretutorials, sfc, snmp4sdn, sxp; see also affinity, toolkit --> + <logback.version>1.1.7</logback.version> + <!-- Used by nic, sfc, sxp, tsdr --> + <mockito.version>1.10.19</mockito.version> + <!-- Used by bgpcep, netconf, ovsdb, sxp; see also opendove, toolkit --> + <netty.version>4.0.37.Final</netty.version> + <!-- Used by openflowjava, persistence, sfc, snmp4sdn, sxp; see also affinity, + toolkit --> + <slf4j.version>1.7.21</slf4j.version> + <!-- Used in integration/distribution; see also affinity, snmp4sdn, toolkit, + tsdr, ttp --> + <spring.version>3.2.14.RELEASE</spring.version> + + + + <!-- Extra support libraries used by SDN-C --> + <equinox.osgi.version>3.8.1.v20120830-144521</equinox.osgi.version> + <jackson-annotations-version>${jackson.version}</jackson-annotations-version> + <jettison.version>1.3.7</jettison.version> + <jvnet.jaxb2.version>0.6.4</jvnet.jaxb2.version> + <apache.httpcomponents.version>4.4</apache.httpcomponents.version> + <antlr.version>4.5.1</antlr.version> + <mysql.connector.version>5.1.39</mysql.connector.version> + <h2database.version>1.4.186</h2database.version> + </properties> <modelVersion>4.0.0</modelVersion> @@ -154,7 +227,7 @@ <additionalDependency> <groupId>org.opendaylight.odlparent</groupId> <artifactId>odlparent</artifactId> - <version>1.6.2-Beryllium-SR2</version> + <version>1.7.1-Boron-SR1</version> </additionalDependency> <additionalDependency> <groupId>org.slf4j</groupId> @@ -215,7 +288,7 @@ <additionalDependency> <groupId>org.opendaylight.mdsal</groupId> <artifactId>yang-binding</artifactId> - <version>${odl.yangtools.version}</version> + <version>${odl.mdsal.yang.binding.version}</version> </additionalDependency> <additionalDependency> <groupId>org.opendaylight.yangtools</groupId> @@ -535,7 +608,7 @@ <additionalDependency> <groupId>org.opendaylight.mdsal</groupId> <artifactId>yang-binding</artifactId> - <version>${odl.yangtools.version}</version> + <version>${odl.mdsal.yang.binding.version}</version> </additionalDependency> <additionalDependency> <groupId>org.opendaylight.yangtools</groupId> 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 @@ <dependency> <groupId>org.opendaylight.mdsal</groupId> <artifactId>yang-binding</artifactId> - <version>${odl.yangtools.version}</version> + <version>${odl.mdsal.yang.binding.version}</version> </dependency> <dependency> <groupId>org.opendaylight.yangtools</groupId> 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<String> 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<String> 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<String> 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<String> 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<TerminalNode> 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<TerminalNode> 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<String, String> parmMap = new HashMap<String, String>(); Set<Map.Entry<String, SvcLogicExpression>> 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<SvcLogicExpression> 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<String, String> 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<String, String> 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<String, String> 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<String, String> parmMap = new HashMap<String, String>(); + 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<String, String> parmMap = new HashMap<String, String>(); + 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<String, String> parmMap = new HashMap<String, String>(); + 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<String, String> 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 @@ <artifactId>commons-lang3</artifactId> <version>3.1</version> </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-library</artifactId> + <version>1.3</version> + <scope>test</scope> + </dependency> </dependencies> <build> 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<String, String> 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. + * <p> + * 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<String,String> 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<String,String> keys_values = new HashMap<String,String>(); + 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. - * <p> - * 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<String,String> 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<String,String> 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<String, String> parameters, SvcLogicContext ctx ) throws SvcLogicException { - checkParameters(parameters, new String[]{"list","delimiter"}, LOG); - ArrayList<SortableCtxListElement> 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<String,String> 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<String, String> 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<String, String> parameters, SvcLogicContext ctx ) throws SvcLogicException { + checkParameters(parameters, new String[]{"list","delimiter"}, LOG); + ArrayList<SortableCtxListElement> list = new ArrayList<SortableCtxListElement>(); + + 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<String,String> 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. - * <p> - * Calls either String.substring(String beginIndex) or - * String.substring(String beginInded, String endIndex) if the end-index - * is present or not. - * @param parameters HashMap<String,String> of parameters passed by the DG to this function - * <table border="1"> - * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> - * <tbody> - * <tr><td>string</td><td>Mandatory</td><td>String to perform substring on</td></tr> - * <tr><td>result</td><td>Mandatory</td><td>Key in context memory to populate the resulting string in</td></tr> - * <tr><td>begin-index</td><td>Mandatory</td><td>Beginning index to pass to Java substring function</td></tr> - * <tr><td>end-index</td><td>Optional</td><td>Ending index to pass to Java substring function. If not included, String.substring(begin) will be called.</td></tr> - * </tbody> - * </table> + * 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<String, String> 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<String, String> 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. + * <p> + * Calls either String.substring(String beginIndex) or + * String.substring(String beginInded, String endIndex) if the end-index + * is present or not. + * @param parameters HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>string</td><td>Mandatory</td><td>String to perform substring on</td></tr> + * <tr><td>result</td><td>Mandatory</td><td>Key in context memory to populate the resulting string in</td></tr> + * <tr><td>begin-index</td><td>Mandatory</td><td>Beginning index to pass to Java substring function</td></tr> + * <tr><td>end-index</td><td>Optional</td><td>Ending index to pass to Java substring function. If not included, String.substring(begin) will be called.</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 8.0.1 + * @see SliPluginUtils#substring(Map, SvcLogicContext) + */ + @Deprecated + public void substring( Map<String, String> 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. + * <p> + * 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<String, String> 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. - * <p> - * 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<String,String> 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<String> Keys = new ArrayList<String>( 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())}. + * <p> + * 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<String> Keys = new ArrayList<String>(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<String, String>} 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<String, String>} containing all the key-value pairs + * in ctx whose key begins with prefix. + */ + public static final Map<String, String> ctxGetBeginsWith( SvcLogicContext ctx, String prefix ) { + Map<String, String> prefixMap = new HashMap<String, String>(); + + 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<String, ?>} 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<String, ?> entries ) { + for( Map.Entry<String, ?> 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(). + * <p> + * 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 <O> 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 <O extends Object> 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. + * <p> + * 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. + * <p> + * 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. + * <p> + * 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<String> keys = new ArrayList<String>(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<String,String> parameters, Logger log, LogLevel loglevel ) { + logLevelIsEnabled( log, loglevel ); + + for( Map.Entry<String,String> 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<SortableCtxListElement> { + HashMap<String,String> child_elements = new HashMap<String,String>(); + 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<String> 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())}. - * <p> - * 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<String> 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<String, String>} 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<String, String>} containing all the key-value pairs - * in ctx whose key begins with prefix. - */ - public static final Map<String, String> ctxGetBeginsWith( SvcLogicContext ctx, String prefix ) { - Map<String, String> 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<String, ?>} 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<String, ?> entries ) { - for( Map.Entry<String, ?> 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(). - * <p> - * 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 <O> 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<String, String> 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<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>prefix</td><td>Optional</td><td>the prefix will be added to each parameter</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 */ - public static final <O extends Object> 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<String, String> 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<String> ctx, Set<String> 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. - * <p> - * 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. - * <p> - * 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<String, String> 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. - * <p> - * 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<String> 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<String,String> parameters, Logger log, LogLevel loglevel ) { - logLevelIsEnabled( log, loglevel ); - - for( Map.Entry<String,String> 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<SortableCtxListElement> { - HashMap<String,String> 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<ServiceRegistration> registrations = new LinkedList<>(); + @SuppressWarnings("rawtypes") private List<ServiceRegistration> registrations = new LinkedList<ServiceRegistration>(); 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<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>original_string</td><td>Mandatory</td><td>String to perform split on</td></tr> + * <tr><td>regex</td><td>Mandatory</td><td>the delimiting regular expression</td></tr> + * <tr><td>limit</td><td>Optional</td><td>result threshold. See String.split method for further description. Defaults to 0</td></tr> + * <tr><td>ctx_memory_result_key</td><td>Mandatory</td><td>Key in context memory to populate the resulting array of strings under</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + * @see String#split(String, int) + */ + public void split( Map<String, String> 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<String,String> of parameters passed by the DG to this function + * emits a true or false outcome + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>source</td><td>Mandatory</td><td>source string</td></tr> + * <tr><td>target</td><td>Mandatory</td><td>target string</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static String equalsIgnoreCase(Map<String, String> 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<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>source</td><td>Mandatory</td><td>source string</td></tr> + * <tr><td>outputPath</td><td>Mandatory</td><td>the location in context memory the result is written to</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void toUpper(Map<String, String> 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<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>source</td><td>Mandatory</td><td>source string</td></tr> + * <tr><td>outputPath</td><td>Mandatory</td><td>the location in context memory the result is written to</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void toLower(Map<String, String> 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<String,String> of parameters passed by the DG to this function + * emits a true or false outcome + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>source</td><td>Mandatory</td><td>source string</td></tr> + * <tr><td>target</td><td>Mandatory</td><td>target string</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static String contains(Map<String, String> 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<String,String> of parameters passed by the DG to this function + * emits a true or false outcome + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>source</td><td>Mandatory</td><td>source string</td></tr> + * <tr><td>target</td><td>Mandatory</td><td>target string</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static String endsWith(Map<String, String> 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<String,String> of parameters passed by the DG to this function + * emits a true or false outcome + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>source</td><td>Mandatory</td><td>source string</td></tr> + * <tr><td>target</td><td>Mandatory</td><td>target string</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static String startsWith(Map<String, String> 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<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>source</td><td>Mandatory</td><td>source string</td></tr> + * <tr><td>outputPath</td><td>Mandatory</td><td>the location in context memory the result is written to</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void trim(Map<String, String> 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<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>source</td><td>Mandatory</td><td>source string</td></tr> + * <tr><td>outputPath</td><td>Mandatory</td><td>the location in context memory the result is written to</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void getLength(Map<String, String> 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<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>source</td><td>Mandatory</td><td>source string</td></tr> + * <tr><td>target</td><td>Mandatory</td><td>The sequence of char values to be replaced</td></tr> + * <tr><td>replacement</td><td>Mandatory</td><td>The replacement sequence of char values</td></tr> + * <tr><td>outputPath</td><td>Mandatory</td><td>the location in context memory the result is written to</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void replace(Map<String, String> 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. + * <p> + * Calls either String.substring(String beginIndex) or + * String.substring(String beginInded, String endIndex) if the end-index + * is present or not. + * @param parameters HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>string</td><td>Mandatory</td><td>String to perform substring on</td></tr> + * <tr><td>result</td><td>Mandatory</td><td>Key in context memory to populate the resulting string in</td></tr> + * <tr><td>begin-index</td><td>Mandatory</td><td>Beginning index to pass to Java substring function</td></tr> + * <tr><td>end-index</td><td>Optional</td><td>Ending index to pass to Java substring function. If not included, String.substring(begin) will be called.</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public void substring( Map<String, String> 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. + * <p> + * Will concat target to source and write the result to outputPath + * @param parameters HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>source</td><td>Mandatory</td><td>source string</td></tr> + * <tr><td>target</td><td>Mandatory</td><td>The sequence of char values to be replaced</td></tr> + * <tr><td>outputPath</td><td>Mandatory</td><td>the location in context memory the result is written to</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + */ + public static void concat( Map<String, String> 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. + * <p> + * Will url encode the source and write the result to outputPath + * @param parameters HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead> + * <tbody> + * <tr><td>source</td><td>Mandatory</td><td>source string</td></tr> + * <tr><td>encoding</td><td>Optional</td><td>the name of a supported character encoding, defaulted to UTF-8 if not supplied</td></tr> + * <tr><td>outputPath</td><td>Mandatory</td><td>the location in context memory the result is written to</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + */ + public static void urlEncode(Map<String, String> 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<HashMap<String, String>>(capacity); for( int i = 0; i < capacity; i++ ) { this.list.add(i, new HashMap<String,String>()); } // Populate "elements" in list String prefix_bracket = this.prefix + '['; - for( String key : new HashSet<>(ctx.getAttributeKeySet()) ) { + for (String key : new HashSet<String>(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<String,String> 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<String, String> parametersMap = new HashMap<String, String>(); + String[] requiredParams = null; + Logger Log = LoggerFactory.getLogger(SliPluginUtils.class); + SliPluginUtils.checkParameters(parametersMap, requiredParams, Log); + } + + @Test(expected = SvcLogicException.class) + public void emptyParametersMap() throws Exception { + Map<String, String> parametersMap = new HashMap<String, String>(); + 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<String, String> parametersMap = new HashMap<String, String>(); + 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<String, String> parameters = new HashMap<String, String>(); + 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<String, String> parameters = new HashMap<String, String>(); + 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<String, String> parameters = new HashMap<String, String>(); + 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<String, String> parameters = new HashMap<String, String>(); + + 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<String, String> parameters = new HashMap<String, String>(); + 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<String, String> parameters; + + @Before + public void setUp() throws Exception { + this.ctx = new SvcLogicContext(); + parameters = new HashMap<String, String>(); + } + + // 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<String, String> 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<String, Object> entries = new HashMap<String, Object>(); + 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<String, String> parameters; + Random rand = new Random(); + + @Before + public void setUp() throws Exception { + this.ctx = new SvcLogicContext(); + this.parameters = new HashMap<String, String>(); + } + + @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<String, String> param; + private SliStringUtils stringUtils = new SliStringUtils(); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + this.ctx = new SvcLogicContext(); + param = new HashMap<String, String>(); + } + + /** + * @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 @@ <version>1.1.0-SNAPSHOT</version> </parent> <artifactId>sliapi-installer</artifactId> - <name>SLI API - Karaf Installer</name> <packaging>pom</packaging> <properties> 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 @@ <dependency> <groupId>org.opendaylight.mdsal</groupId> <artifactId>maven-sal-api-gen-plugin</artifactId> - <version>${odl.yangtools.version}</version> + <version>${odl.sal.api.gen.plugin.version}</version> <type>jar</type> </dependency> </dependencies> @@ -58,7 +58,7 @@ <dependency> <groupId>org.opendaylight.mdsal</groupId> <artifactId>yang-binding</artifactId> - <version>${odl.yangtools.version}</version> + <version>${odl.mdsal.yang.binding.version}</version> </dependency> <dependency> <groupId>org.opendaylight.yangtools</groupId> 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 @@ <dependency> <groupId>org.opendaylight.mdsal</groupId> <artifactId>maven-sal-api-gen-plugin</artifactId> - <version>${odl.yangtools.version}</version> + <version>${odl.sal.api.gen.plugin.version}</version> <type>jar</type> </dependency> <dependency> 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.<ExecuteGraphOutput> 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.<ExecuteGraphOutput> 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.<ExecuteGraphOutput> 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.<HealthcheckOutput> 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.<HealthcheckOutput> 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 |