├── .github └── workflows │ ├── publish-central.yml │ └── publish-docs.yml ├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── CHANGELOG.md ├── README.md ├── jitpack.yml ├── license.txt ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── asciidoc │ ├── about.adoc │ ├── built-in-listeners.adoc │ ├── changelog-1.10.x.adoc │ ├── changelog-1.2.x.adoc │ ├── changelog-1.3.x.adoc │ ├── changelog-1.4.x.adoc │ ├── changelog-1.5.adoc │ ├── changelog-1.6.adoc │ ├── changelog-1.7.adoc │ ├── changelog-1.8.x.adoc │ ├── changelog-1.9.x.adoc │ ├── changelog.adoc │ ├── development.adoc │ ├── docinfos │ │ └── index-docinfo.html │ ├── how-to-use.adoc │ ├── index.adoc │ ├── installation.adoc │ ├── listeners.adoc │ └── sample-config.adoc ├── java │ └── net │ │ └── ttddyy │ │ └── dsproxy │ │ ├── ConnectionIdManager.java │ │ ├── ConnectionInfo.java │ │ ├── DataSourceProxyException.java │ │ ├── ExecutionInfo.java │ │ ├── QueryCount.java │ │ ├── QueryCountHolder.java │ │ ├── QueryInfo.java │ │ ├── QueryType.java │ │ ├── StatementType.java │ │ ├── listener │ │ ├── ChainListener.java │ │ ├── CompositeMethodListener.java │ │ ├── DataSourceQueryCountListener.java │ │ ├── MethodExecutionContext.java │ │ ├── MethodExecutionListener.java │ │ ├── NoOpMethodExecutionListener.java │ │ ├── NoOpQueryExecutionListener.java │ │ ├── QueryCountStrategy.java │ │ ├── QueryExecutionListener.java │ │ ├── QueryUtils.java │ │ ├── SingleQueryCountHolder.java │ │ ├── SlowQueryListener.java │ │ ├── ThreadQueryCountHolder.java │ │ ├── TracingMethodListener.java │ │ ├── lifecycle │ │ │ ├── CallableStatementMethodCallbacks.java │ │ │ ├── ConnectionMethodCallbacks.java │ │ │ ├── DataSourceMethodCallbacks.java │ │ │ ├── JdbcLifecycleEventExecutionListener.java │ │ │ ├── JdbcLifecycleEventListener.java │ │ │ ├── JdbcLifecycleEventListenerAdapter.java │ │ │ ├── JdbcLifecycleEventListenerUtils.java │ │ │ ├── PreparedStatementMethodCallbacks.java │ │ │ ├── ResultSetMethodCallbacks.java │ │ │ ├── StatementMethodCallbacks.java │ │ │ └── WrapperMethodCallbacks.java │ │ └── logging │ │ │ ├── AbstractQueryLogEntryCreator.java │ │ │ ├── AbstractQueryLoggingListener.java │ │ │ ├── AbstractSlowQueryLoggingListener.java │ │ │ ├── CommonsLogLevel.java │ │ │ ├── CommonsQueryLoggingListener.java │ │ │ ├── CommonsSlowQueryListener.java │ │ │ ├── DefaultJsonQueryLogEntryCreator.java │ │ │ ├── DefaultQueryLogEntryCreator.java │ │ │ ├── JULQueryLoggingListener.java │ │ │ ├── JULSlowQueryListener.java │ │ │ ├── Log4jLogLevel.java │ │ │ ├── Log4jQueryLoggingListener.java │ │ │ ├── Log4jSlowQueryListener.java │ │ │ ├── LoggingCondition.java │ │ │ ├── OutputParameterJsonLogEntryCreator.java │ │ │ ├── OutputParameterLogEntryCreator.java │ │ │ ├── ParameterValueConverter.java │ │ │ ├── QueryLogEntryCreator.java │ │ │ ├── RegisterOutParameterValueConverter.java │ │ │ ├── SLF4JLogLevel.java │ │ │ ├── SLF4JQueryLoggingListener.java │ │ │ ├── SLF4JSlowQueryListener.java │ │ │ ├── SetNullParameterValueConverter.java │ │ │ ├── SystemOutQueryLoggingListener.java │ │ │ └── SystemOutSlowQueryListener.java │ │ ├── proxy │ │ ├── CachedRowSetResultSetProxyLogic.java │ │ ├── CachedRowSetResultSetProxyLogicFactory.java │ │ ├── ConnectionProxyLogic.java │ │ ├── DataSourceNameAware.java │ │ ├── DataSourceProxyLogic.java │ │ ├── DefaultConnectionIdManager.java │ │ ├── GeneratedKeysUtils.java │ │ ├── GlobalConnectionIdManager.java │ │ ├── JdbcProxyFactory.java │ │ ├── NanoTimeStopwatchFactory.java │ │ ├── NativeJdbcExtractUtils.java │ │ ├── ObjectArrayUtils.java │ │ ├── ParameterKey.java │ │ ├── ParameterKeyUtils.java │ │ ├── ParameterSetOperation.java │ │ ├── ProxyConfig.java │ │ ├── ProxyJdbcObject.java │ │ ├── ProxyLogicSupport.java │ │ ├── ReflectionUtils.java │ │ ├── RepeatableReadResultSetProxyLogic.java │ │ ├── RepeatableReadResultSetProxyLogicFactory.java │ │ ├── ResultSetProxyLogic.java │ │ ├── ResultSetProxyLogicFactory.java │ │ ├── SimpleResultSetProxyLogic.java │ │ ├── SimpleResultSetProxyLogicFactory.java │ │ ├── StatementMethodNames.java │ │ ├── StatementProxyLogic.java │ │ ├── Stopwatch.java │ │ ├── StopwatchFactory.java │ │ ├── SystemStopwatchFactory.java │ │ └── jdk │ │ │ ├── CallableStatementInvocationHandler.java │ │ │ ├── ConnectionInvocationHandler.java │ │ │ ├── DataSourceInvocationHandler.java │ │ │ ├── JdkJdbcProxyFactory.java │ │ │ ├── PreparedStatementInvocationHandler.java │ │ │ ├── ResultSetInvocationHandler.java │ │ │ └── StatementInvocationHandler.java │ │ ├── support │ │ ├── AbstractQueryCountLoggingHandlerInterceptor.java │ │ ├── AbstractQueryCountLoggingRequestListener.java │ │ ├── AbstractQueryCountLoggingServletFilter.java │ │ ├── BeanNameProxyDataSource.java │ │ ├── CommonsLogUtils.java │ │ ├── CommonsQueryCountLoggingHandlerInterceptor.java │ │ ├── CommonsQueryCountLoggingRequestListener.java │ │ ├── CommonsQueryCountLoggingServletFilter.java │ │ ├── DataSourceProxyNativeJdbcExtractor.java │ │ ├── DefaultQueryCountLogEntryCreator.java │ │ ├── JULQueryCountLoggingHandlerInterceptor.java │ │ ├── JULQueryCountLoggingRequestListener.java │ │ ├── JULQueryCountLoggingServletFilter.java │ │ ├── Log4jLogUtils.java │ │ ├── Log4jQueryCountLoggingHandlerInterceptor.java │ │ ├── Log4jQueryCountLoggingRequestListener.java │ │ ├── Log4jQueryCountLoggingServletFilter.java │ │ ├── ProxyConfigSpringXmlSupport.java │ │ ├── ProxyConnectionAdvice.java │ │ ├── ProxyDataSource.java │ │ ├── ProxyDataSourceBuilder.java │ │ ├── QueryCountLogEntryCreator.java │ │ ├── QueryCountLoggerBuilder.java │ │ ├── QueryCounterClearFilter.java │ │ ├── QueryCounterClearHandlerInterceptor.java │ │ ├── QueryCounterClearServletRequestListener.java │ │ ├── SLF4JLogUtils.java │ │ ├── SLF4JQueryCountLoggingHandlerInterceptor.java │ │ ├── SLF4JQueryCountLoggingRequestListener.java │ │ ├── SLF4JQueryCountLoggingServletFilter.java │ │ ├── SystemOutQueryCountLoggingHandlerInterceptor.java │ │ ├── SystemOutQueryCountLoggingRequestListener.java │ │ ├── SystemOutQueryCountLoggingServletFilter.java │ │ ├── jndi │ │ │ └── ProxyDataSourceObjectFactory.java │ │ └── tags │ │ │ └── MetricsTag.java │ │ └── transform │ │ ├── NoOpParameterTransformer.java │ │ ├── NoOpQueryTransformer.java │ │ ├── ParameterReplacer.java │ │ ├── ParameterTransformer.java │ │ ├── QueryTransformer.java │ │ └── TransformInfo.java └── resources │ └── META-INF │ └── dsproxy.tld └── test └── java └── net └── ttddyy └── dsproxy ├── DataSourceQueryCountListenerTest.java ├── ExecutionInfoBuilder.java ├── ExecutionInfoTest.java ├── GeneratedKeysProxyTest.java ├── LastQueryListener.java ├── PreparedStatementQueryTest.java ├── ProxyDataSourceTest.java ├── QueryInfoBuilder.java ├── ResultSetProxyTest.java ├── StatementInvocationHandlerTest.java ├── StatementQueryTest.java ├── StatementTypeTest.java ├── TestListener.java ├── TestUtils.java ├── listener ├── CallCheckMethodExecutionListener.java ├── MethodExecutionListenerTest.java ├── QueryUtilsTest.java ├── SingleQueryCountHolderTest.java ├── SlowQueryListenerTest.java ├── ThreadQueryCountHolderTest.java ├── TracingMethodListenerTest.java ├── lifecycle │ ├── JdbcLifecycleEventExecutionListenerTest.java │ └── JdbcLifecycleEventListenerUtilsTest.java └── logging │ ├── CommonsQueryLoggingListenerTest.java │ ├── CommonsSlowQueryListenerTest.java │ ├── DefaultJsonQueryLogEntryCreatorTest.java │ ├── DefaultQueryLogEntryCreatorTest.java │ ├── InMemoryCommonsLog.java │ ├── InMemoryJULLogger.java │ ├── InMemorySLF4JLogger.java │ ├── JULQueryLoggingListenerTest.java │ ├── JULSlowQueryListenerTest.java │ ├── Log4jQueryLoggingListenerTest.java │ ├── LoggingListenerLogLevelTest.java │ ├── LoggingListenerTest.java │ ├── OutputParameterJsonLogEntryCreatorTest.java │ ├── OutputParameterLogEntryCreatorTest.java │ ├── RegisterOutParameterValueConverterTest.java │ ├── SLF4JQueryLoggingListenerTest.java │ ├── SLF4JSlowQueryListenerTest.java │ └── SetNullParameterValueConverterTest.java ├── proxy ├── CachedRowSetResultSetProxyLogicTest.java ├── ConnectionProxyLogicMockTest.java ├── DataSourceProxyLogicMockTest.java ├── DefaultConnectionIdManagerTest.java ├── GlobalConnectionIdManagerTest.java ├── JdkJdbcProxyFactoryTest.java ├── MockTestUtils.java ├── NativeJdbcExtractUtilsTest.java ├── ObjectArrayUtilsTest.java ├── ParameterKeyTest.java ├── ProxyLogicSupportTest.java ├── ProxyObjectEqualityTest.java ├── RepeatableReadResultSetProxyLogicTest.java ├── SimpleResultSetProxyLogicTest.java ├── StatementProxyLogicForCallableStatementMockTest.java ├── StatementProxyLogicForPreparedStatementMockTest.java └── StatementProxyLogicMockTest.java ├── support ├── DataSourceProxyNativeJdbcExtractorMockTest.java ├── DefaultQueryCountLogEntryCreatorTest.java ├── ProxyConfigBuilderTest.java └── ProxyDataSourceBuilderTest.java └── transform ├── CallableStatementParameterTransformTest.java ├── CallableStatementQueryTransformerTest.java ├── ParameterReplacerTest.java ├── PreparedStatementParameterTransformTest.java ├── PreparedStatementQueryTransformTest.java ├── StatementQueryTransformTest.java ├── TransformInfoForParametersTest.java └── TransformInfoForQueryTest.java /.github/workflows/publish-central.yml: -------------------------------------------------------------------------------- 1 | name: Release to Maven Central (Deploy Snapshot) 2 | 3 | on: 4 | push: 5 | branches: [master, 1.x, release] 6 | 7 | jobs: 8 | release: 9 | if: github.repository == 'jdbc-observations/datasource-proxy' 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Set up Maven Central Repository 14 | uses: actions/setup-java@v3 15 | with: 16 | java-version: 8 17 | distribution: temurin 18 | server-id: central 19 | server-username: MAVEN_USERNAME 20 | server-password: MAVEN_PASSWORD 21 | gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} 22 | gpg-passphrase: MAVEN_GPG_PASSPHRASE 23 | - name: Publish package 24 | run: ./mvnw --batch-mode deploy -DskipTests=true -Pgithub-central 25 | env: 26 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 27 | MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 28 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} 29 | 30 | -------------------------------------------------------------------------------- /.github/workflows/publish-docs.yml: -------------------------------------------------------------------------------- 1 | name: Publish documentation to the project page 2 | 3 | on: 4 | push: 5 | branches: [ 1.x, release ] 6 | 7 | jobs: 8 | publish: 9 | if: github.repository == 'jdbc-observations/datasource-proxy' 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Set up code 14 | uses: actions/setup-java@v3 15 | with: 16 | java-version: 8 17 | distribution: temurin 18 | 19 | - name: Get project version 20 | run: | 21 | VERSION=$( ./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout ) 22 | echo "project_version=$VERSION" >> $GITHUB_ENV 23 | 24 | - name: Process asciidoc and javadoc 25 | run: ./mvnw -Ppublish-doc asciidoctor:process-asciidoc@output-html javadoc:javadoc 26 | 27 | # 28 | # construct a directory to be copied to "gh-pages" branch 29 | # target/deploy-documents/ -- map to "docs" dir in "gh-pages" 30 | # `-- -- e.g. "1.7", "1.8-SNAPSHOT" 31 | # `-- user-guide/ 32 | # `-- api/ 33 | # `-- snapshot -- for latest snapshot from main 34 | # `-- user-guide/ 35 | # `-- api/ 36 | # `-- current -- for latest release version 37 | # `-- user-guide/ 38 | # `-- api/ 39 | 40 | - name: Prepare "snapshot" documents 41 | if: "github.ref == 'refs/heads/1.x' && contains(env.project_version, 'snapshot')" 42 | run: | 43 | mkdir -p target/deploy-documents/snapshot/user-guide/ 44 | mkdir -p target/deploy-documents/snapshot/api/ 45 | cp -Rf target/generated-docs/index.html target/deploy-documents/snapshot/user-guide/ 46 | cp -Rf target/site/apidocs/* target/deploy-documents/snapshot/api/ 47 | 48 | - name: Prepare "project-version" documents 49 | run: | 50 | mkdir -p target/deploy-documents/${{ env.project_version }}/user-guide/ 51 | mkdir -p target/deploy-documents/${{ env.project_version }}/api/ 52 | cp -Rf target/generated-docs/* target/deploy-documents/${{ env.project_version }}/user-guide/ 53 | cp -Rf target/site/apidocs/* target/deploy-documents/${{ env.project_version }}/api/ 54 | 55 | - name: Prepare "current" documents 56 | if: "github.ref == 'refs/heads/release'" 57 | run: | 58 | mkdir -p target/deploy-documents/current/user-guide/ 59 | mkdir -p target/deploy-documents/current/api/ 60 | cp -Rf target/generated-docs/index.html target/deploy-documents/current/user-guide/ 61 | cp -Rf target/site/apidocs/* target/deploy-documents/current/api/ 62 | 63 | 64 | - name: Deploy documents 65 | uses: peaceiris/actions-gh-pages@v3 66 | with: 67 | github_token: ${{ secrets.GITHUB_TOKEN }} 68 | publish_branch: gh-pages 69 | publish_dir: target/deploy-documents 70 | destination_dir: docs 71 | keep_files: true 72 | full_commit_message: "Deploying documents(${{ env.project_version}}) to ${{ github.ref }} from ${{ github.repository }}@${{ github.sha }}" 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | *.ipr 4 | *.iws 5 | .idea/ 6 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdbc-observations/datasource-proxy/081d6d7e4c479e4a5340605d7241f29d6d0c1d49/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # datasource-proxy 2 | 3 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/net.ttddyy/datasource-proxy/badge.svg)][maven-central_badge] 4 | 5 | 6 | ## About 7 | 8 | Provide proxy classes for JDBC API to intercept executing queries and methods. 9 | 10 | ### Versions 11 | 12 | - 2.x _(Under development)_ 13 | - ~~Java8 baseline.~~ 14 | - `master` branch 15 | 16 | - 1.x 17 | - Works with JDK1.6+ (works well with Java8 and above). 18 | - `1.x` branch 19 | 20 | ## User Guide 21 | 22 | - [Current Release Version][user-guide-current] 23 | - [Snapshot Version][user-guide-snapshot] 24 | - [Older Version](https://github.com/jdbc-observations/datasource-proxy/wiki/User-Guide) 25 | 26 | ## Maven 27 | 28 | ```xml 29 | 30 | net.ttddyy 31 | datasource-proxy 32 | [LATEST_VERSION] 33 | 34 | ``` 35 | 36 | - latest version is: [![Maven Central](https://maven-badges.herokuapp.com/maven-central/net.ttddyy/datasource-proxy/badge.svg)][maven-central_badge] 37 | - No dependencies to other libraries, everything is optional. 38 | - For example, if you want to use slf4j logger with `SLF4JQueryLoggingListener`, then you need slf4j library. 39 | - requires jdk1.6+ 40 | 41 | Snapshots are available via the Maven Central repository. 42 | For instructions on how to consume snapshot releases, please refer to [the official documentation](https://central.sonatype.org/publish/publish-portal-snapshots/#consuming-snapshot-releases-for-your-project). 43 | 44 | 45 | ## Related Projects 46 | 47 | *Examples:* 48 | - [datasource-proxy-examples][datasource-proxy-examples] 49 | 50 | *Unit test support:* 51 | - [datasource-assert][datasource-assert] 52 | 53 | 54 | ## Javadoc 55 | 56 | - [Current Release Version][javadoc-current] 57 | - [Snapshot Version][javadoc-snapshot] 58 | - [Older Version](https://github.com/jdbc-observations/datasource-proxy/wiki/Javadoc) 59 | 60 | 61 | ---- 62 | 63 | [maven-central_badge]: https://maven-badges.herokuapp.com/maven-central/net.ttddyy/datasource-proxy/ 64 | [user-guide-current]: http://jdbc-observations.github.io/datasource-proxy/docs/current/user-guide/ 65 | [user-guide-snapshot]: http://jdbc-observations.github.io/datasource-proxy/docs/snapshot/user-guide/ 66 | [javadoc-current]: http://jdbc-observations.github.io/datasource-proxy/docs/current/api/ 67 | [javadoc-snapshot]: http://jdbc-observations.github.io/datasource-proxy/docs/snapshot/api/ 68 | [datasource-proxy-examples]: https://github.com/ttddyy/datasource-proxy-examples 69 | [datasource-assert]: https://github.com/ttddyy/datasource-assert 70 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | # 2 | # config for https://jitpack.io 3 | # 4 | jdk: 5 | - oraclejdk8 -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2009-2017 Tadaya Tsuyukubo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/asciidoc/changelog-1.10.x.adoc: -------------------------------------------------------------------------------- 1 | [[changelog-1.10.1]] 2 | === 1.10.1 3 | ==== Improvements 4 | * Use monotonic time to calculate the elapsed time (https://github.com/jdbc-observations/datasource-proxy/issues/110[Issue-110]). 5 | * Create `RowSetFactory` only at initialization (https://github.com/jdbc-observations/datasource-proxy/issues/115[Issue-115]). + 6 | Thanks _https://github.com/reda-alaoui[Réda Housni Alaoui]_ for this contribution!! 7 | 8 | ==== Bug Fixes 9 | * Do not create a proxy when `ResultSet` is `null` (https://github.com/jdbc-observations/datasource-proxy/issues/117[Issue-117]). 10 | 11 | 12 | [[changelog-1.10]] 13 | === 1.10 14 | 15 | ==== New Features 16 | 17 | * Added the ability to create a pure proxy `DataSource` object (https://github.com/jdbc-observations/datasource-proxy/issues/102[Issue-102]). 18 | + 19 | The `ProxyDataSource` is a concrete implementation of the `DataSource` class, not a proxy object. 20 | To create pure proxies all the way from `DataSource` to `ResultSet`, a new method `ProxyDataSourceBuilder#buildProxy()` has been introduced. 21 | This `buildProxy()` method returns a proxy `DataSource` object and utilizes `DataSourceProxyLogic` to handle the proxy calls. 22 | 23 | ==== Improvements 24 | 25 | * Proxy instance is now retrievable from `MethodExecutionContext#getProxy()` (https://github.com/jdbc-observations/datasource-proxy/issues/108[Issue-108]). 26 | 27 | * Improved handling of `equals` for proxy objects (https://github.com/jdbc-observations/datasource-proxy/issues/105[Issue-105]). 28 | 29 | * Added `Automatic-Module-Name` (https://github.com/jdbc-observations/datasource-proxy/issues/101[Issue-101]). 30 | 31 | * Added `ProxyJdbcObject#getProxyConfig` (https://github.com/jdbc-observations/datasource-proxy/issues/103[Issue-103]). 32 | 33 | * Isolation level retrieval from the connection is now an opt-in feature (https://github.com/jdbc-observations/datasource-proxy/issues/109[Issue-109]). 34 | + 35 | It is disabled by default and does not call `Connection#getTransactionIsolation()`. 36 | To enable isolation level retrieval, use `ProxyDataSourceBuilder#retrieveIsolation()`. 37 | `ProxyDataSourceBuilder#writeIsolation()` is effective only when `ProxyDataSourceBuilder#retrieveIsolation()` is called. 38 | + 39 | [source,java] 40 | ---- 41 | ProxyDataSourceBuilder.create(ds).retrieveIsolation().writeIsolation().build(); 42 | ---- 43 | 44 | * Documentation about the Jakarta namespace (https://github.com/jdbc-observations/datasource-proxy/issues/96[Issue-96]). 45 | 46 | ==== Bug Fixes 47 | 48 | * Properly call the before/after method callback on `ProxyDataSource#getConnection()` (https://github.com/jdbc-observations/datasource-proxy/issues/100[Issue-100]). 49 | -------------------------------------------------------------------------------- /src/main/asciidoc/changelog-1.2.x.adoc: -------------------------------------------------------------------------------- 1 | [[changelog-1.2.1]] 2 | === 1.2.1 3 | * fixed prepared statement getting already executed queries in listener (https://github.com/ttddyy/datasource-proxy/issues/9[Issue #9]) 4 | 5 | 6 | [[changelog-1.2]] 7 | === 1.2 8 | * QueryTransformer and ParameterTransformer for query and parameter replacement 9 | 10 | -------------------------------------------------------------------------------- /src/main/asciidoc/changelog-1.3.x.adoc: -------------------------------------------------------------------------------- 1 | [[changelog-1.3.3]] 2 | === 1.3.3 3 | * update `DefaultQueryLogEntryCreator` to allow subclasses to override log entry details 4 | 5 | 6 | [[changelog-1.3.2]] 7 | === 1.3.2 8 | 9 | * add `CommonsOracleOutputParameterLoggingListener` 10 | * add new listener for oracle to log output params. `CommonsOracleOutputParameterLoggingListener` 11 | 12 | 13 | [[changelog-1.3.1]] 14 | === 1.3.1 15 | 16 | * make logger name configurable in `CommonsQueryLoggingListener` and `SLF4JQueryLoggingListener` 17 | * `setNull` and `registerOutParameter` receives descriptive string value in `QueryInfo#getQueryArgsList` (temporal implementation) 18 | * `ExecutionInfo` will have access to the statement/prepared/callable object used by the execution 19 | 20 | 21 | [[changelog-1.3]] 22 | === 1.3 23 | 24 | * update minimum jdk to java6+ 25 | * add java8 new jdbc API (JDBC 4.2) 26 | * new JNDI support class: `ProxyDataSourceObjectFactory` 27 | * new fluent API builder: `ProxyDataSourceBuilder` 28 | 29 | * logging: 30 | ** update log format 31 | ** add json format 32 | ** more entries: statement-type, batch, batch-size 33 | ** new logger for System.Out 34 | 35 | * change metric names: 36 | call => total, elapsedTime => time, added success, failure, etc. 37 | 38 | * rename `~QueryCountLoggingFilter` to `~QueryCountServletFilter` 39 | * remove deprecated methods 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/asciidoc/changelog-1.5.adoc: -------------------------------------------------------------------------------- 1 | [[changelog-1.5.1]] 2 | === 1.5.1 3 | 4 | ==== New Features and Improvements 5 | 6 | * Add custom value store on `ExecutionInfo` and `MethodExecutionContext` 7 | Mainly used for passing values between before and after listener callback. 8 | 9 | * `Stopwatch` and `StopwatchFactory` are added for elapsed time calculation. + 10 | Elapsed time calculation for query(`ExecutionInfo#getElapsedTime`) and method(`MethodExecutionContext.getElapsedTime()`) 11 | have been updated to use `Stopwatch` class. + 12 | `Stopwatch` is created by `StopwatchFactory`, and it is configurable in `ProxyConfig`. + 13 | Two implementation classes are available - `SystemStopwatchFactory` which creates `SystemStopwatch` and 14 | `NanoTimeStopwatchFactory` which creates `NanoTimeStopwatch`. + 15 | `SystemStopwatch` uses `System.currentTimeMillis()` and `NanoTimeStopwatch` uses `System.nanoTime()` to calculate 16 | elapsed time. Default is set to `SystemStopwatchFactory`. 17 | 18 | NOTE: `SlowQueryListener` needs to set `StopwatchFactory` independently from `ProxyConfig` in order to calculate 19 | `ExecutionInfo#getElapsedTime()` for running slow queries. 20 | 21 | 22 | ==== Bug Fixes 23 | 24 | * Fix `NullPointerException` when `Object` methods(`toString()`, `hashCode()`, etc) is called on `JdbcLifecycleEventListener`. 25 | 26 | 27 | 28 | [[changelog-1.5]] 29 | === 1.5 30 | 31 | * `JdbcLifecycleEventListener` is added. + 32 | This listener defines before/after callback methods for all of proxy classes(`DataSource`, 33 | `Connection`, `Statement`, `PreparedStatement`, `CallableStatement`, and `ResultSet`). + 34 | See details on <>. 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/asciidoc/changelog-1.6.adoc: -------------------------------------------------------------------------------- 1 | [[changelog-1.6]] 2 | === 1.6 3 | 4 | ==== Improvements 5 | 6 | * `GlobalConnectionIdManager` is added. This id-manager generates unique connection ids(sequentially increasing number) 7 | across datasources within JVM. 8 | 9 | * On `JdbcLifecycleEventListener`, method names have updated to map invoked method name only. 10 | Classname has removed in order to improve the interception for overloaded methods. 11 | + 12 | -- 13 | The callback method is determined by invoked method name. 14 | See the details on below _"Breaking Changes"_ section. 15 | -- 16 | 17 | ==== Breaking Changes 18 | 19 | * `JdbcLifecycleEventListener` method names have changed. + 20 | (e.g.: `beforeGetConnectionOnDataSource()` => `beforeGetConnection()`) 21 | + 22 | -- 23 | Previously, the method names on the listener had convention of: 24 | `[before|after]On` + 25 | e.g.: `beforeGetConnectionOnDataSource()` + 26 | This has changed to `[before|after]` + 27 | e.g.: `beforeGetConnection()` 28 | 29 | For overloaded methods (e.g.: `Statement#execute` and `PreparedStatement#execute`), please reference the 30 | documentation: <> 31 | -- 32 | -------------------------------------------------------------------------------- /src/main/asciidoc/changelog-1.7.adoc: -------------------------------------------------------------------------------- 1 | [[changelog-1.7]] 2 | === 1.7 3 | 4 | ==== Improvements 5 | 6 | * When `asJson()` is called on `ProxyDataSourceBuilder`, slow listeners also write the output 7 | as json. (https://github.com/ttddyy/datasource-proxy/pull/66[Issue-66]) 8 | 9 | * Support `DataSource` that implements `AutoCloseable`. (https://github.com/ttddyy/datasource-proxy/pull/68[Issue-68]) 10 | -------------------------------------------------------------------------------- /src/main/asciidoc/changelog-1.8.x.adoc: -------------------------------------------------------------------------------- 1 | [[changelog-1.8.1]] 2 | === 1.8.1 3 | ==== Improvements 4 | * Improve `RepeatableReadResultSetProxyLogic` 5 | ** Support `wasNull` (https://github.com/ttddyy/datasource-proxy/pull/87[Issue-87]) 6 | ** Support calling different numeric type get methods on the same index (https://github.com/ttddyy/datasource-proxy/pull/88[Issue-88]) 7 | 8 | + 9 | Thanks _https://github.com/reda-alaoui[Réda Housni Alaoui]_ for this contribution!! 10 | 11 | 12 | [[changelog-1.8]] 13 | === 1.8 14 | 15 | ==== New Features 16 | 17 | * Add `Isolation level` in query logs. (https://github.com/ttddyy/datasource-proxy/pull/74[Issue-74]) 18 | + 19 | Logging the isolation level is disabled by default. + 20 | To enable, call `logIsolationLevel(true)` on `ProxyDataSourceBuilder`. 21 | + 22 | [source,java] 23 | ---- 24 | ProxyDataSourceBuilder.create(datasource) 25 | .logQueryBySlf4j(SLF4JLogLevel.INFO) 26 | .logIsolationLevel(true) // <== 27 | .build(); 28 | ---- 29 | + 30 | Thanks _https://github.com/sleepo581[Alexei Brinza]_ for this contribution!! 31 | 32 | * Add support for Log4j2. (https://github.com/ttddyy/datasource-proxy/pull/78[Issue-78]) + 33 | Thanks _https://github.com/ivasanpag[Ivan Jose Sanchez Pagador]_ for this contribution!! 34 | 35 | ==== Improvements 36 | * Add `ProxyDataSource#getDataSource()` method to return the original data source. (https://github.com/ttddyy/datasource-proxy/issues/81[Issue-81]) 37 | 38 | ==== Bug Fixes 39 | 40 | * Support `isWrapperFor` and `unwrap` for `ProxyDataSource` itself (https://github.com/ttddyy/datasource-proxy/issues/82[Issue-82]) 41 | -------------------------------------------------------------------------------- /src/main/asciidoc/changelog-1.9.x.adoc: -------------------------------------------------------------------------------- 1 | [[changelog-1.9]] 2 | === 1.9 3 | 4 | ==== New Features 5 | 6 | * Add `formatQuery()` method on `ProxyDataSourceBuilder`, which you can specify a callback to modify the query for logging. For example, apply a query formatter. (https://github.com/jdbc-observations/datasource-proxy/pull/94[Issue-94]) 7 | + 8 | [source,java] 9 | ---- 10 | Formatter formatter = FormatStyle.BASIC.getFormatter(); 11 | DataSource ds = ProxyDataSourceBuilder 12 | .create(actualDataSource) 13 | .formatQuery(formatter::format) 14 | .logQueryToSysOut() 15 | .build() 16 | ---- 17 | + 18 | Thanks _https://github.com/rvullriede[Raphael Vullriede]_ for this contribution!! 19 | -------------------------------------------------------------------------------- /src/main/asciidoc/changelog.adoc: -------------------------------------------------------------------------------- 1 | [[changelog]] 2 | == Changelog 3 | 4 | :numbered!: 5 | 6 | include::changelog-1.10.x.adoc[] 7 | 8 | include::changelog-1.9.x.adoc[] 9 | 10 | include::changelog-1.8.x.adoc[] 11 | 12 | include::changelog-1.7.adoc[] 13 | 14 | include::changelog-1.6.adoc[] 15 | 16 | include::changelog-1.5.adoc[] 17 | 18 | include::changelog-1.4.x.adoc[] 19 | 20 | include::changelog-1.3.x.adoc[] 21 | 22 | include::changelog-1.2.x.adoc[] 23 | 24 | :numbered: 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main/asciidoc/development.adoc: -------------------------------------------------------------------------------- 1 | [[development]] 2 | == Development 3 | 4 | Github repository: {datasource-proxy} 5 | 6 | === Build Documentation 7 | 8 | Generate `index.html` 9 | 10 | ```sh 11 | > ./mvnw asciidoctor:process-asciidoc@output-html 12 | ``` 13 | 14 | Http preview 15 | 16 | ```sh 17 | > ./mvnw asciidoctor:http@output-html 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /src/main/asciidoc/docinfos/index-docinfo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/main/asciidoc/index.adoc: -------------------------------------------------------------------------------- 1 | [[user-guide]] 2 | = datasource-proxy User Guide 3 | Tadaya Tsuyukubo 4 | // ====================================================================================== 5 | :sectnums: 6 | // 7 | :docinfodir: docinfos 8 | // 9 | // do not populate last updated time 10 | :last-update-label!: 11 | // 12 | // add analytics only when publishing docs to github 13 | ifdef::insert-analytics[] 14 | :docinfo: private-head 15 | endif::[] 16 | // 17 | :oss-snapshot-repository: https://oss.sonatype.org/content/repositories/snapshots/net/ttddyy/datasource-proxy/ 18 | :datasource-proxy: https://github.com/jdbc-observations/datasource-proxy 19 | :datasource-proxy-examples: https://github.com/ttddyy/datasource-proxy-examples 20 | :jndi-embedded-example: {datasource-proxy-examples}/tree/master/jndi-embedded-example 21 | :jndi-external-example: {datasource-proxy-examples}/tree/master/jndi-external-example 22 | 23 | 24 | include::about.adoc[] 25 | include::installation.adoc[] 26 | include::how-to-use.adoc[] 27 | include::listeners.adoc[] 28 | include::built-in-listeners.adoc[] 29 | include::sample-config.adoc[] 30 | include::development.adoc[] 31 | include::changelog.adoc[] 32 | -------------------------------------------------------------------------------- /src/main/asciidoc/installation.adoc: -------------------------------------------------------------------------------- 1 | [[installation]] 2 | == Installation 3 | 4 | === Dependency 5 | 6 | ```xml 7 | 8 | net.ttddyy 9 | datasource-proxy 10 | [LATEST_VERSION] 11 | 12 | ``` 13 | 14 | There is no dependent library for using `datasource-proxy`. + 15 | If you choose to use logging support for specific logging library, such as commons or slf4j, you need to specify 16 | the dependency explicitly. 17 | 18 | `datasource-proxy` does not bring any libraries via transitive dependency. 19 | 20 | 21 | ==== Snapshot 22 | 23 | Snapshot is available via {oss-snapshot-repository}[oss sonatype snapshot repository]. 24 | 25 | To download snapshot jars, enable sonatype snapshot repository: 26 | 27 | ```xml 28 | 29 | 30 | sonatype-snapshots-repo 31 | https://oss.sonatype.org/content/repositories/snapshots 32 | 33 | false 34 | 35 | 36 | true 37 | 38 | 39 | 40 | ``` 41 | 42 | === Supported Java Versions 43 | 44 | `datasource-proxy` works with java 1.6+. + 45 | Java8(_JDBC4.2_) works fine, and planning to support java9(_JDBC 4.3_) specific feature. 46 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/ConnectionIdManager.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | import java.sql.Connection; 4 | import java.util.Set; 5 | 6 | /** 7 | * @author Tadaya Tsuyukubo 8 | * @since 1.4.2 9 | */ 10 | public interface ConnectionIdManager { 11 | 12 | String getId(Connection connection); 13 | 14 | /** 15 | * Notify this manager that id of corresponding connection has addClosedId. 16 | * 17 | * @param closedId addClosedId connection id 18 | * @since 1.4.5 19 | */ 20 | void addClosedId(String closedId); 21 | 22 | /** 23 | * Return set of connection ids that have not yet addClosedId. 24 | * 25 | * @return set of open connection ids 26 | * @since 1.4.5 27 | */ 28 | Set getOpenConnectionIds(); 29 | 30 | } -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/ConnectionInfo.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | import java.sql.Connection; 4 | 5 | /** 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.4.2 8 | */ 9 | public class ConnectionInfo { 10 | 11 | private String dataSourceName; 12 | private String connectionId; 13 | private int isolationLevel; 14 | private boolean isClosed; 15 | private int commitCount; 16 | private int rollbackCount; 17 | 18 | public String getDataSourceName() { 19 | return dataSourceName; 20 | } 21 | 22 | public void setDataSourceName(String dataSourceName) { 23 | this.dataSourceName = dataSourceName; 24 | } 25 | 26 | public String getConnectionId() { 27 | return connectionId; 28 | } 29 | 30 | public void setConnectionId(String connectionId) { 31 | this.connectionId = connectionId; 32 | } 33 | 34 | public int getIsolationLevel() { 35 | return isolationLevel; 36 | } 37 | 38 | public void setIsolationLevel(int isolationLevel) { 39 | this.isolationLevel = isolationLevel; 40 | } 41 | 42 | /** 43 | * Increment commit count. 44 | * 45 | * @since 1.4.5 46 | */ 47 | public void incrementCommitCount() { 48 | this.commitCount++; 49 | } 50 | 51 | /** 52 | * Increment rollback count. 53 | * 54 | * @since 1.4.5 55 | */ 56 | public void incrementRollbackCount() { 57 | this.rollbackCount++; 58 | } 59 | 60 | /** 61 | * Returns how many times {@link Connection#commit()} method is called. 62 | * 63 | * @return num of commit method being called 64 | * @since 1.4.5 65 | */ 66 | public int getCommitCount() { 67 | return commitCount; 68 | } 69 | 70 | /** 71 | * @param commitCount num of commit method call 72 | * @since 1.4.5 73 | */ 74 | public void setCommitCount(int commitCount) { 75 | this.commitCount = commitCount; 76 | } 77 | 78 | /** 79 | * Returns how many times {@link Connection#rollback()} method is called. 80 | * 81 | * @return num of rollback method being called 82 | * @since 1.4.5 83 | */ 84 | public int getRollbackCount() { 85 | return rollbackCount; 86 | } 87 | 88 | /** 89 | * @param rollbackCount num of rollback method call 90 | * @since 1.4.5 91 | */ 92 | public void setRollbackCount(int rollbackCount) { 93 | this.rollbackCount = rollbackCount; 94 | } 95 | 96 | /** 97 | * @since 1.4.5 98 | */ 99 | public boolean isClosed() { 100 | return isClosed; 101 | } 102 | 103 | /** 104 | * @since 1.4.5 105 | */ 106 | public void setClosed(boolean closed) { 107 | isClosed = closed; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/DataSourceProxyException.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; 4 | 5 | /** 6 | * Framework exception that datasource-proxy encountered. 7 | * 8 | * @author Tadaya Tsuyukubo 9 | * @since 1.4.3 10 | */ 11 | public class DataSourceProxyException extends RuntimeException { 12 | 13 | public DataSourceProxyException() { 14 | } 15 | 16 | public DataSourceProxyException(String message) { 17 | super(message); 18 | } 19 | 20 | public DataSourceProxyException(String message, Throwable cause) { 21 | super(message, cause); 22 | } 23 | 24 | public DataSourceProxyException(Throwable cause) { 25 | super(cause); 26 | } 27 | 28 | @IgnoreJRERequirement 29 | public DataSourceProxyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 30 | super(message, cause, enableSuppression, writableStackTrace); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/QueryCountHolder.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.ConcurrentMap; 9 | 10 | /** 11 | * Hold QueryCount object by datasource name. 12 | * 13 | * @author Tadaya Tsuyukubo 14 | * @see net.ttddyy.dsproxy.listener.QueryCountStrategy 15 | */ 16 | public class QueryCountHolder { 17 | 18 | private static ThreadLocal> queryCountMapHolder = new ThreadLocal>() { 19 | @Override 20 | protected ConcurrentMap initialValue() { 21 | return new ConcurrentHashMap(); 22 | } 23 | }; 24 | 25 | public static QueryCount get(String dataSourceName) { 26 | final Map map = queryCountMapHolder.get(); 27 | return map.get(dataSourceName); 28 | } 29 | 30 | public static QueryCount getGrandTotal() { 31 | final QueryCount totalCount = new QueryCount(); 32 | final Map map = queryCountMapHolder.get(); 33 | for (QueryCount queryCount : map.values()) { 34 | totalCount.setSelect(totalCount.getSelect() + queryCount.getSelect()); 35 | totalCount.setInsert(totalCount.getInsert() + queryCount.getInsert()); 36 | totalCount.setUpdate(totalCount.getUpdate() + queryCount.getUpdate()); 37 | totalCount.setDelete(totalCount.getDelete() + queryCount.getDelete()); 38 | totalCount.setOther(totalCount.getOther() + queryCount.getOther()); 39 | totalCount.setTotal(totalCount.getTotal() + queryCount.getTotal()); 40 | totalCount.setSuccess(totalCount.getSuccess() + queryCount.getSuccess()); 41 | totalCount.setFailure(totalCount.getFailure() + queryCount.getFailure()); 42 | totalCount.setTime(totalCount.getTime() + queryCount.getTime()); 43 | } 44 | return totalCount; 45 | } 46 | 47 | public static void put(String dataSourceName, QueryCount count) { 48 | queryCountMapHolder.get().put(dataSourceName, count); 49 | } 50 | 51 | public static List getDataSourceNamesAsList() { 52 | return new ArrayList(getDataSourceNames()); 53 | } 54 | 55 | public static Set getDataSourceNames() { 56 | return queryCountMapHolder.get().keySet(); 57 | } 58 | 59 | public static void clear() { 60 | queryCountMapHolder.get().clear(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/QueryInfo.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | import net.ttddyy.dsproxy.proxy.ParameterSetOperation; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * Hold query and parameter information. 12 | * 13 | * For Statement batch execution, there will be multiple QueryInfo. 14 | * For Prepared/Callable batch execution, there will be one QueryInfo with multiple elements in parameters. 15 | * For batch execution, single instance of this class represents each batch entry. 16 | * 17 | * @author Tadaya Tsuyukubo 18 | */ 19 | public class QueryInfo { 20 | private String query; 21 | 22 | private List> parametersList = new ArrayList>(); 23 | 24 | public QueryInfo() { 25 | } 26 | 27 | public QueryInfo(String query) { 28 | this.query = query; 29 | } 30 | 31 | public String getQuery() { 32 | return query; 33 | } 34 | 35 | public void setQuery(String query) { 36 | this.query = query; 37 | } 38 | 39 | /** 40 | * Deprecated: Since return doesn't contain method information, {@link #getParametersList()} is now used. 41 | * 42 | * @return list of parameter map, key is first arg as string, value is second arg. 43 | * @deprecated use {@link #getParametersList()} 44 | */ 45 | @Deprecated 46 | public List> getQueryArgsList() { 47 | // simulate old implementation behavior 48 | List> result = new ArrayList>(); 49 | for (List paramsList : this.parametersList) { 50 | Map map = new HashMap(); 51 | 52 | for (ParameterSetOperation param : paramsList) { 53 | Object[] args = param.getArgs(); 54 | map.put(args[0].toString(), args[1]); 55 | } 56 | 57 | result.add(map); 58 | } 59 | return result; 60 | } 61 | 62 | 63 | /** 64 | * List of parameter-operation-list. 65 | * 66 | * For non-batch Prepared/Callable execution, this list contains 1 element that is a list which contains all 67 | * parameter sets operations for the execution. 68 | * For batch Prepared/Callable executions, this list will have N number of elements. 69 | * 70 | * @return list of prameter operation list 71 | * @since 1.4 72 | */ 73 | public List> getParametersList() { 74 | return parametersList; 75 | } 76 | 77 | public void setParametersList(List> parametersList) { 78 | this.parametersList = parametersList; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/QueryType.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | public enum QueryType { 4 | SELECT, INSERT, UPDATE, DELETE, OTHER 5 | } -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/StatementType.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | import java.sql.CallableStatement; 4 | import java.sql.PreparedStatement; 5 | import java.sql.Statement; 6 | 7 | /** 8 | * @author Tadaya Tsuyukubo 9 | * @since 1.3 10 | */ 11 | public enum StatementType { 12 | STATEMENT, PREPARED, CALLABLE; 13 | 14 | /** 15 | * @param statement statement 16 | * @param statement type 17 | * @return statement type 18 | * @since 1.3.1 19 | */ 20 | public static StatementType valueOf(T statement) { 21 | if (statement instanceof CallableStatement) { 22 | return CALLABLE; 23 | } else if (statement instanceof PreparedStatement) { 24 | return PREPARED; 25 | } else { 26 | return STATEMENT; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/ChainListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.QueryInfo; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Execute chain of listeners. 11 | * 12 | * @author Tadaya Tsuyukubo 13 | */ 14 | public class ChainListener implements QueryExecutionListener { 15 | private List listeners = new ArrayList(); 16 | 17 | @Override 18 | public void beforeQuery(ExecutionInfo execInfo, List queryInfoList) { 19 | for (QueryExecutionListener listener : listeners) { 20 | listener.beforeQuery(execInfo, queryInfoList); 21 | } 22 | } 23 | 24 | @Override 25 | public void afterQuery(ExecutionInfo execInfo, List queryInfoList) { 26 | for (QueryExecutionListener listener : listeners) { 27 | listener.afterQuery(execInfo, queryInfoList); 28 | } 29 | } 30 | 31 | public void addListener(QueryExecutionListener listener) { 32 | this.listeners.add(listener); 33 | } 34 | 35 | public List getListeners() { 36 | return listeners; 37 | } 38 | 39 | public void setListeners(List listeners) { 40 | this.listeners = listeners; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/CompositeMethodListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Execute chain of {@link MethodExecutionListener}. 8 | * 9 | * @author Tadaya Tsuyukubo 10 | * @since 1.4.3 11 | */ 12 | public class CompositeMethodListener implements MethodExecutionListener { 13 | private List listeners = new ArrayList(); 14 | 15 | @Override 16 | public void beforeMethod(MethodExecutionContext executionContext) { 17 | for (MethodExecutionListener listener : this.listeners) { 18 | listener.beforeMethod(executionContext); 19 | } 20 | } 21 | 22 | @Override 23 | public void afterMethod(MethodExecutionContext executionContext) { 24 | for (MethodExecutionListener listener : this.listeners) { 25 | listener.afterMethod(executionContext); 26 | } 27 | } 28 | 29 | public boolean addListener(MethodExecutionListener listener) { 30 | return this.listeners.add(listener); 31 | } 32 | 33 | public List getListeners() { 34 | return listeners; 35 | } 36 | 37 | public void setListeners(List listeners) { 38 | this.listeners = listeners; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/MethodExecutionListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | /** 4 | * Callback listener for JDBC API method invocations. 5 | * 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.4.3 8 | */ 9 | public interface MethodExecutionListener { 10 | 11 | MethodExecutionListener DEFAULT = new NoOpMethodExecutionListener(); 12 | 13 | void beforeMethod(MethodExecutionContext executionContext); 14 | 15 | void afterMethod(MethodExecutionContext executionContext); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/NoOpMethodExecutionListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | /** 4 | * No-op implementation of {@link MethodExecutionListener} 5 | * 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.4.3 8 | */ 9 | public class NoOpMethodExecutionListener implements MethodExecutionListener { 10 | 11 | @Override 12 | public void beforeMethod(MethodExecutionContext executionContext) { 13 | // no-op 14 | } 15 | 16 | @Override 17 | public void afterMethod(MethodExecutionContext executionContext) { 18 | // no-op 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/NoOpQueryExecutionListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.QueryInfo; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * No operation implementation of {@link QueryExecutionListener} 10 | * 11 | * @author Tadaya Tsuyukubo 12 | * @since 1.2 13 | */ 14 | public class NoOpQueryExecutionListener implements QueryExecutionListener { 15 | 16 | @Override 17 | public void beforeQuery(ExecutionInfo execInfo, List queryInfoList) { 18 | // do nothing 19 | } 20 | 21 | @Override 22 | public void afterQuery(ExecutionInfo execInfo, List queryInfoList) { 23 | // do nothing 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/QueryCountStrategy.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import net.ttddyy.dsproxy.QueryCount; 4 | 5 | /** 6 | * @author Tadaya Tsuyukubo 7 | * @see DataSourceQueryCountListener 8 | * @since 1.4.2 9 | */ 10 | public interface QueryCountStrategy { 11 | 12 | QueryCount getOrCreateQueryCount(String dataSourceName); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/QueryExecutionListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.QueryInfo; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Listener interface. Inject the implementation to proxy handler interceptors. 10 | * 11 | * @author Tadaya Tsuyukubo 12 | * @see ChainListener 13 | * @see net.ttddyy.dsproxy.proxy.jdk.ConnectionInvocationHandler 14 | * @see net.ttddyy.dsproxy.proxy.jdk.PreparedStatementInvocationHandler 15 | * @see net.ttddyy.dsproxy.proxy.jdk.StatementInvocationHandler 16 | */ 17 | public interface QueryExecutionListener { 18 | 19 | static QueryExecutionListener DEFAULT = new NoOpQueryExecutionListener(); 20 | 21 | void beforeQuery(ExecutionInfo execInfo, List queryInfoList); 22 | 23 | void afterQuery(ExecutionInfo execInfo, List queryInfoList); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/QueryUtils.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import net.ttddyy.dsproxy.QueryType; 4 | 5 | /** 6 | * @author Tadaya Tsuyukubo 7 | */ 8 | public class QueryUtils { 9 | public static String removeCommentAndWhiteSpace(String query) { 10 | if (query == null) { 11 | return null; 12 | } 13 | return query.replaceAll("--.*\n", "").replaceAll("\n", "").replaceAll("/\\*.*\\*/", "").trim(); 14 | } 15 | 16 | /** 17 | * Returns type of query from given query string. 18 | * 19 | * @param query a query string 20 | * @return type of query 21 | * @since 1.4 22 | */ 23 | public static QueryType getQueryType(String query) { 24 | 25 | final String trimmedQuery = removeCommentAndWhiteSpace(query); 26 | if (trimmedQuery == null || trimmedQuery.length() < 1) { 27 | return QueryType.OTHER; 28 | } 29 | 30 | final char firstChar = trimmedQuery.charAt(0); 31 | final QueryType type; 32 | switch (firstChar) { 33 | case 'S': 34 | case 's': 35 | type = QueryType.SELECT; 36 | break; 37 | case 'I': 38 | case 'i': 39 | type = QueryType.INSERT; 40 | break; 41 | case 'U': 42 | case 'u': 43 | type = QueryType.UPDATE; 44 | break; 45 | case 'D': 46 | case 'd': 47 | type = QueryType.DELETE; 48 | break; 49 | default: 50 | type = QueryType.OTHER; 51 | } 52 | return type; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/SingleQueryCountHolder.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import net.ttddyy.dsproxy.QueryCount; 4 | import net.ttddyy.dsproxy.QueryCountHolder; 5 | 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.concurrent.ConcurrentMap; 8 | 9 | /** 10 | * Use single instance to hold {@link net.ttddyy.dsproxy.QueryCount}. 11 | * 12 | * The {@link QueryCount} holds total accumulated values from all threads where database access has performed. 13 | * 14 | * When {@link #populateQueryCountHolder} is set to {@code true}(default), it populates {@link QueryCountHolder}. 15 | * 16 | * @author Tadaya Tsuyukubo 17 | * @since 1.4.2 18 | */ 19 | public class SingleQueryCountHolder implements QueryCountStrategy { 20 | 21 | private ConcurrentMap queryCountMap = new ConcurrentHashMap(); 22 | private boolean populateQueryCountHolder = true; 23 | 24 | @Override 25 | public QueryCount getOrCreateQueryCount(String dataSourceName) { 26 | QueryCount queryCount = queryCountMap.get(dataSourceName); 27 | if (queryCount == null) { 28 | queryCountMap.putIfAbsent(dataSourceName, new QueryCount()); 29 | queryCount = queryCountMap.get(dataSourceName); 30 | } 31 | if (this.populateQueryCountHolder) { 32 | QueryCountHolder.put(dataSourceName, queryCount); 33 | } 34 | return queryCount; 35 | } 36 | 37 | public ConcurrentMap getQueryCountMap() { 38 | return queryCountMap; 39 | } 40 | 41 | public void setQueryCountMap(ConcurrentMap queryCountMap) { 42 | this.queryCountMap = queryCountMap; 43 | } 44 | 45 | public boolean isPopulateQueryCountHolder() { 46 | return populateQueryCountHolder; 47 | } 48 | 49 | public void setPopulateQueryCountHolder(boolean populateQueryCountHolder) { 50 | this.populateQueryCountHolder = populateQueryCountHolder; 51 | } 52 | 53 | public void clear() { 54 | this.queryCountMap.clear(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/ThreadQueryCountHolder.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import net.ttddyy.dsproxy.QueryCount; 4 | import net.ttddyy.dsproxy.QueryCountHolder; 5 | 6 | /** 7 | * Uses {@link QueryCountHolder} which uses thread local to hold {@link QueryCount}. 8 | * 9 | * @author Tadaya Tsuyukubo 10 | * @since 1.4.2 11 | */ 12 | public class ThreadQueryCountHolder implements QueryCountStrategy { 13 | 14 | @Override 15 | public QueryCount getOrCreateQueryCount(String dataSourceName) { 16 | QueryCount queryCount = QueryCountHolder.get(dataSourceName); 17 | if (queryCount == null) { 18 | queryCount = new QueryCount(); 19 | QueryCountHolder.put(dataSourceName, queryCount); 20 | } 21 | return queryCount; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/lifecycle/DataSourceMethodCallbacks.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.lifecycle; 2 | 3 | import net.ttddyy.dsproxy.listener.MethodExecutionContext; 4 | 5 | /** 6 | * Defines callback methods for {@link javax.sql.DataSource}. 7 | * 8 | * @author Tadaya Tsuyukubo 9 | * @see javax.sql.DataSource 10 | * @see javax.sql.CommonDataSource 11 | * @since 1.5 12 | */ 13 | public interface DataSourceMethodCallbacks { 14 | 15 | // 16 | // CommonDataSource methods 17 | // 18 | 19 | void beforeGetLoginTimeout(MethodExecutionContext executionContext); 20 | 21 | void beforeGetLogWriter(MethodExecutionContext executionContext); 22 | 23 | void beforeGetParentLogger(MethodExecutionContext executionContext); 24 | 25 | void beforeSetLoginTimeout(MethodExecutionContext executionContext); 26 | 27 | void beforeSetLogWriter(MethodExecutionContext executionContext); 28 | 29 | void afterGetLoginTimeout(MethodExecutionContext executionContext); 30 | 31 | void afterGetLogWriter(MethodExecutionContext executionContext); 32 | 33 | void afterGetParentLogger(MethodExecutionContext executionContext); 34 | 35 | void afterSetLoginTimeout(MethodExecutionContext executionContext); 36 | 37 | void afterSetLogWriter(MethodExecutionContext executionContext); 38 | 39 | 40 | // 41 | // DataSource methods 42 | // 43 | 44 | void beforeGetConnection(MethodExecutionContext executionContext); 45 | 46 | void afterGetConnection(MethodExecutionContext executionContext); 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/lifecycle/JdbcLifecycleEventExecutionListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.lifecycle; 2 | 3 | import net.ttddyy.dsproxy.DataSourceProxyException; 4 | import net.ttddyy.dsproxy.ExecutionInfo; 5 | import net.ttddyy.dsproxy.QueryInfo; 6 | import net.ttddyy.dsproxy.listener.MethodExecutionContext; 7 | import net.ttddyy.dsproxy.listener.MethodExecutionListener; 8 | import net.ttddyy.dsproxy.listener.QueryExecutionListener; 9 | 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.lang.reflect.Method; 12 | import java.util.List; 13 | 14 | /** 15 | * Holder for {@link JdbcLifecycleEventListener} and adapt it to {@link MethodExecutionListener} and {@link QueryExecutionListener}. 16 | * 17 | * @author Tadaya Tsuyukubo 18 | * @since 1.5 19 | */ 20 | public class JdbcLifecycleEventExecutionListener implements MethodExecutionListener, QueryExecutionListener { 21 | 22 | private JdbcLifecycleEventListener delegate; 23 | 24 | public JdbcLifecycleEventExecutionListener(JdbcLifecycleEventListener delegate) { 25 | this.delegate = delegate; 26 | } 27 | 28 | @Override 29 | public void beforeMethod(MethodExecutionContext executionContext) { 30 | this.delegate.beforeMethod(executionContext); 31 | methodCallback(executionContext, true); 32 | } 33 | 34 | @Override 35 | public void afterMethod(MethodExecutionContext executionContext) { 36 | methodCallback(executionContext, false); 37 | this.delegate.afterMethod(executionContext); 38 | } 39 | 40 | @Override 41 | public void beforeQuery(ExecutionInfo execInfo, List queryInfoList) { 42 | this.delegate.beforeQuery(execInfo, queryInfoList); 43 | } 44 | 45 | @Override 46 | public void afterQuery(ExecutionInfo execInfo, List queryInfoList) { 47 | this.delegate.afterQuery(execInfo, queryInfoList); 48 | } 49 | 50 | private void methodCallback(MethodExecutionContext methodContext, boolean isBefore) { 51 | 52 | // dynamically invoke corresponding callback method on JdbcLifecycleEventListener. 53 | 54 | String methodName = methodContext.getMethod().getName(); 55 | Method lifecycleMethod = JdbcLifecycleEventListenerUtils.getListenerMethod(methodName, isBefore); 56 | 57 | if (lifecycleMethod == null) { 58 | // when there is no corresponding life cycle callback, just skip it. 59 | // This happens when method on Object is called. e.g.: toString(), hashCode(), etc. 60 | return; 61 | } 62 | 63 | try { 64 | lifecycleMethod.invoke(this.delegate, methodContext); 65 | } catch (InvocationTargetException ex) { 66 | throw new DataSourceProxyException(ex.getTargetException()); 67 | } catch (Exception ex) { 68 | throw new DataSourceProxyException(ex); 69 | } 70 | 71 | } 72 | 73 | public void setDelegate(JdbcLifecycleEventListener delegate) { 74 | this.delegate = delegate; 75 | } 76 | 77 | public JdbcLifecycleEventListener getDelegate() { 78 | return delegate; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/lifecycle/JdbcLifecycleEventListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.lifecycle; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.QueryInfo; 5 | import net.ttddyy.dsproxy.listener.MethodExecutionContext; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Callback for all JDBC proxy methods. 11 | * 12 | * This interface provides before and after method callbacks for all JDBC proxy interfaces({@link javax.sql.DataSource}, 13 | * {@link java.sql.Connection}, {@link java.sql.Statement}, {@link java.sql.PreparedStatement}, 14 | * {@link java.sql.CallableStatement}, {@link java.sql.ResultSet}), as well as 15 | * callbacks for any method calls ({@link #beforeMethod(MethodExecutionContext)}, {@link #afterMethod(MethodExecutionContext)}) 16 | * and query executions({@link #beforeQuery(ExecutionInfo, List)}, {@link #afterQuery(ExecutionInfo, List)}). 17 | * 18 | * @author Tadaya Tsuyukubo 19 | * @see JdbcLifecycleEventExecutionListener 20 | * @see JdbcLifecycleEventListenerAdapter 21 | * @since 1.5 22 | */ 23 | public interface JdbcLifecycleEventListener extends WrapperMethodCallbacks, DataSourceMethodCallbacks, ConnectionMethodCallbacks, 24 | StatementMethodCallbacks, PreparedStatementMethodCallbacks, CallableStatementMethodCallbacks, 25 | ResultSetMethodCallbacks { 26 | 27 | void beforeMethod(MethodExecutionContext executionContext); 28 | 29 | void afterMethod(MethodExecutionContext executionContext); 30 | 31 | void beforeQuery(ExecutionInfo execInfo, List queryInfoList); 32 | 33 | void afterQuery(ExecutionInfo execInfo, List queryInfoList); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/lifecycle/WrapperMethodCallbacks.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.lifecycle; 2 | 3 | import net.ttddyy.dsproxy.listener.MethodExecutionContext; 4 | 5 | /** 6 | * Defines callback methods for {@link java.sql.Wrapper}. 7 | * 8 | * @author Tadaya Tsuyukubo 9 | * @since 1.6 10 | */ 11 | public interface WrapperMethodCallbacks { 12 | 13 | void beforeIsWrapperFor(MethodExecutionContext executionContext); 14 | 15 | void beforeUnwrap(MethodExecutionContext executionContext); 16 | 17 | void afterIsWrapperFor(MethodExecutionContext executionContext); 18 | 19 | void afterUnwrap(MethodExecutionContext executionContext); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/AbstractSlowQueryLoggingListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.QueryInfo; 5 | import net.ttddyy.dsproxy.listener.SlowQueryListener; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Abstract class to log slow query. 11 | * 12 | * This class delegates actual log writing to subclasses. 13 | * 14 | * @author Tadaya Tsuyukubo 15 | * @since 1.4.1 16 | */ 17 | public abstract class AbstractSlowQueryLoggingListener extends SlowQueryListener { 18 | 19 | protected boolean writeDataSourceName = true; 20 | protected boolean writeConnectionId = true; 21 | protected boolean writeIsolation; 22 | protected QueryLogEntryCreator queryLogEntryCreator = new DefaultQueryLogEntryCreator(); 23 | protected String prefix; 24 | 25 | @Override 26 | protected void onSlowQuery(ExecutionInfo execInfo, List queryInfoList, long startTimeInMills) { 27 | String entry = this.queryLogEntryCreator.getLogEntry(execInfo, queryInfoList, this.writeDataSourceName, this.writeConnectionId, this.writeIsolation); 28 | if (this.prefix != null) { 29 | StringBuilder sb = new StringBuilder(); 30 | sb.append(this.prefix); 31 | sb.append(entry); 32 | entry = sb.toString(); 33 | } 34 | writeLog(entry); 35 | } 36 | 37 | protected abstract void writeLog(String message); 38 | 39 | 40 | public void setQueryLogEntryCreator(QueryLogEntryCreator queryLogEntryCreator) { 41 | this.queryLogEntryCreator = queryLogEntryCreator; 42 | } 43 | 44 | public QueryLogEntryCreator getQueryLogEntryCreator() { 45 | return queryLogEntryCreator; 46 | } 47 | 48 | /** 49 | * @since 1.4.2 50 | */ 51 | public void setWriteDataSourceName(boolean writeDataSourceName) { 52 | this.writeDataSourceName = writeDataSourceName; 53 | } 54 | 55 | /** 56 | * @since 1.4.2 57 | */ 58 | public void setWriteConnectionId(boolean writeConnectionId) { 59 | this.writeConnectionId = writeConnectionId; 60 | } 61 | 62 | /** 63 | * @since 1.8 64 | */ 65 | public void setWriteIsolation(boolean writeIsolation) { 66 | this.writeIsolation = writeIsolation; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/CommonsLogLevel.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | /** 4 | * Apache Commons-Logging log level representation. 5 | * 6 | * @author Tadaya Tsuyukubo 7 | */ 8 | public enum CommonsLogLevel { 9 | // least serious to most serious 10 | TRACE, DEBUG, INFO, WARN, ERROR, FATAL; 11 | 12 | public static CommonsLogLevel nullSafeValueOf(String name) { 13 | if (name == null) { 14 | return null; 15 | } 16 | return valueOf(name); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/CommonsQueryLoggingListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.support.CommonsLogUtils; 4 | import org.apache.commons.logging.Log; 5 | import org.apache.commons.logging.LogFactory; 6 | 7 | /** 8 | * Record executed query information using Commons-Logging. 9 | * 10 | * @author Tadaya Tsuyukubo 11 | */ 12 | public class CommonsQueryLoggingListener extends AbstractQueryLoggingListener { 13 | 14 | protected Log log = LogFactory.getLog(CommonsQueryLoggingListener.class); 15 | protected CommonsLogLevel logLevel = CommonsLogLevel.DEBUG; // default DEBUG 16 | 17 | 18 | public CommonsQueryLoggingListener() { 19 | 20 | // initialize logging condition that checks current log level 21 | this.loggingCondition = new LoggingCondition() { 22 | @Override 23 | public boolean getAsBoolean() { 24 | switch (logLevel) { 25 | case DEBUG: 26 | return log.isDebugEnabled(); 27 | case ERROR: 28 | return log.isErrorEnabled(); 29 | case FATAL: 30 | return log.isFatalEnabled(); 31 | case INFO: 32 | return log.isInfoEnabled(); 33 | case TRACE: 34 | return log.isTraceEnabled(); 35 | case WARN: 36 | return log.isWarnEnabled(); 37 | } 38 | return false; 39 | } 40 | }; 41 | 42 | } 43 | 44 | @Override 45 | protected void writeLog(String message) { 46 | CommonsLogUtils.writeLog(log, this.logLevel, message); 47 | } 48 | 49 | public void setLogLevel(CommonsLogLevel logLevel) { 50 | this.logLevel = logLevel; 51 | } 52 | 53 | /** 54 | * @deprecated use {{@link #setLog(String)}} 55 | */ 56 | @Override 57 | @Deprecated 58 | protected void resetLogger(String loggerName) { 59 | this.log = LogFactory.getLog(loggerName); 60 | } 61 | 62 | /** 63 | * Override {@link Log} instance with specified log name. 64 | * 65 | * @param logName log name 66 | * @since 1.4.1 67 | */ 68 | public void setLog(String logName) { 69 | this.log = LogFactory.getLog(logName); 70 | } 71 | 72 | /** 73 | * Override {@link Log} instance. 74 | * 75 | * @param log new log instance 76 | * @since 1.4.1 77 | */ 78 | public void setLog(Log log) { 79 | this.log = log; 80 | } 81 | 82 | /** 83 | * @return log 84 | * @since 1.4.1 85 | */ 86 | public Log getLog() { 87 | return log; 88 | } 89 | 90 | /** 91 | * @return log level to write 92 | * @since 1.4.1 93 | */ 94 | public CommonsLogLevel getLogLevel() { 95 | return logLevel; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/CommonsSlowQueryListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.support.CommonsLogUtils; 4 | import org.apache.commons.logging.Log; 5 | import org.apache.commons.logging.LogFactory; 6 | 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | * Log slow query using Commons-Logging. 11 | * 12 | * @author Tadaya Tsuyukubo 13 | * @since 1.4.1 14 | */ 15 | public class CommonsSlowQueryListener extends AbstractSlowQueryLoggingListener { 16 | 17 | protected Log log = LogFactory.getLog(CommonsSlowQueryListener.class); 18 | protected CommonsLogLevel logLevel = CommonsLogLevel.WARN; // default WARN 19 | 20 | public CommonsSlowQueryListener() { 21 | } 22 | 23 | public CommonsSlowQueryListener(long threshold, TimeUnit thresholdTimeUnit) { 24 | this.threshold = threshold; 25 | this.thresholdTimeUnit = thresholdTimeUnit; 26 | } 27 | 28 | @Override 29 | protected void writeLog(String message) { 30 | CommonsLogUtils.writeLog(this.log, this.logLevel, message); 31 | } 32 | 33 | public void setLogLevel(CommonsLogLevel logLevel) { 34 | this.logLevel = logLevel; 35 | } 36 | 37 | public void setLog(String logName) { 38 | setLog(LogFactory.getLog(logName)); 39 | } 40 | 41 | public void setLog(Log log) { 42 | this.log = log; 43 | } 44 | 45 | public Log getLog() { 46 | return log; 47 | } 48 | 49 | public CommonsLogLevel getLogLevel() { 50 | return logLevel; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/JULQueryLoggingListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | /** 7 | * Log executed query information using JUL(Java Util Logging). 8 | * 9 | * @author Tadaya Tsuyukubo 10 | * @since 1.4 11 | */ 12 | public class JULQueryLoggingListener extends AbstractQueryLoggingListener { 13 | 14 | protected Logger logger = Logger.getLogger(JULQueryLoggingListener.class.getName()); 15 | protected Level logLevel = Level.FINE; // default FINE 16 | 17 | public JULQueryLoggingListener() { 18 | // initialize condition that checks the current logger level 19 | this.loggingCondition = new LoggingCondition() { 20 | @Override 21 | public boolean getAsBoolean() { 22 | return logger.isLoggable(logLevel); 23 | } 24 | }; 25 | } 26 | 27 | @Override 28 | protected void writeLog(String message) { 29 | this.logger.log(this.logLevel, message); 30 | } 31 | 32 | public void setLogLevel(Level logLevel) { 33 | this.logLevel = logLevel; 34 | } 35 | 36 | /** 37 | * @deprecated use {{@link #setLogger(String)}} 38 | */ 39 | @Override 40 | @Deprecated 41 | protected void resetLogger(String loggerName) { 42 | this.logger = Logger.getLogger(loggerName); 43 | } 44 | 45 | /** 46 | * Override {@link Logger} instance that has specified logger name 47 | * 48 | * @param loggerName new logger name 49 | * @since 1.4.1 50 | */ 51 | public void setLogger(String loggerName) { 52 | this.logger = Logger.getLogger(loggerName); 53 | } 54 | 55 | /** 56 | * Override {@link Logger} instance. 57 | * 58 | * @param logger new logger instance 59 | * @since 1.4.1 60 | */ 61 | public void setLogger(Logger logger) { 62 | this.logger = logger; 63 | } 64 | 65 | /** 66 | * @return logger 67 | * @since 1.4.1 68 | */ 69 | public Logger getLogger() { 70 | return logger; 71 | } 72 | 73 | /** 74 | * @return log level to write 75 | * @since 1.4.1 76 | */ 77 | public Level getLogLevel() { 78 | return logLevel; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/JULSlowQueryListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | 7 | /** 8 | * Log slow query using JUL(Java Util Logging). 9 | * 10 | * @author Tadaya Tsuyukubo 11 | * @since 1.4.1 12 | */ 13 | public class JULSlowQueryListener extends AbstractSlowQueryLoggingListener { 14 | 15 | protected Logger logger = Logger.getLogger(JULSlowQueryListener.class.getName()); 16 | protected Level logLevel = Level.WARNING; // default WARNING 17 | 18 | public JULSlowQueryListener() { 19 | } 20 | 21 | public JULSlowQueryListener(long threshold, TimeUnit thresholdTimeUnit) { 22 | this.threshold = threshold; 23 | this.thresholdTimeUnit = thresholdTimeUnit; 24 | } 25 | 26 | @Override 27 | protected void writeLog(String message) { 28 | this.logger.log(this.logLevel, message); 29 | } 30 | 31 | public void setLogLevel(Level logLevel) { 32 | this.logLevel = logLevel; 33 | } 34 | 35 | public void setLogger(String loggerName) { 36 | setLogger(Logger.getLogger(loggerName)); 37 | } 38 | 39 | public void setLogger(Logger logger) { 40 | this.logger = logger; 41 | } 42 | 43 | public Logger getLogger() { 44 | return logger; 45 | } 46 | 47 | public Level getLogLevel() { 48 | return logLevel; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/Log4jLogLevel.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | /** 4 | * Apache Commons Logging log4j log level representation. 5 | * 6 | * @author Ivan Jose Sanchez Pagador 7 | * @since 1.8 8 | */ 9 | public enum Log4jLogLevel { 10 | // least serious to most serious 11 | TRACE, DEBUG, INFO, WARN, ERROR; 12 | 13 | public static Log4jLogLevel nullSafeValueOf(String name) { 14 | if (name == null) { 15 | return null; 16 | } 17 | return valueOf(name); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/Log4jQueryLoggingListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.support.Log4jLogUtils; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | /** 8 | * Log executed query information using Apache Commons Logging log4j.. 9 | * 10 | * @author Ivan Jose Sanchez Pagador 11 | * @since 1.8 12 | */ 13 | public class Log4jQueryLoggingListener extends AbstractQueryLoggingListener { 14 | 15 | protected Logger log = LogManager.getLogger(Log4jQueryLoggingListener.class.getName()); 16 | protected Log4jLogLevel logLevel = Log4jLogLevel.DEBUG; // default DEBUG 17 | 18 | public Log4jQueryLoggingListener() { 19 | this.loggingCondition = new LoggingCondition() { 20 | @Override 21 | public boolean getAsBoolean() { 22 | switch (logLevel) { 23 | case TRACE: 24 | return log.isTraceEnabled(); 25 | case DEBUG: 26 | return log.isDebugEnabled(); 27 | case INFO: 28 | return log.isInfoEnabled(); 29 | case WARN: 30 | return log.isWarnEnabled(); 31 | case ERROR: 32 | return log.isErrorEnabled(); 33 | } 34 | return false; 35 | } 36 | }; 37 | } 38 | 39 | @Override 40 | protected void writeLog(String message) { 41 | Log4jLogUtils.writeLog(log, this.logLevel, message); 42 | } 43 | 44 | public void setLogLevel(Log4jLogLevel logLevel) { 45 | this.logLevel = logLevel; 46 | } 47 | 48 | /** 49 | * @deprecated use {{@link #setLogger(String)}} 50 | */ 51 | @Override 52 | @Deprecated 53 | protected void resetLogger(String loggerName) { 54 | this.log = LogManager.getLogger(loggerName); 55 | } 56 | 57 | /** 58 | * Override {@link Logger} instance that has specified logger name. 59 | * 60 | * @param log log name 61 | */ 62 | public void setLogger(String log) { 63 | this.log = LogManager.getLogger(log); 64 | } 65 | 66 | /** 67 | * Override {@link Logger} instance. 68 | * 69 | * @param log new LogManager instance 70 | */ 71 | public void setLogger(Logger log) { 72 | this.log = log; 73 | } 74 | 75 | /** 76 | * @return logger 77 | */ 78 | public Logger getLogger() { 79 | return log; 80 | } 81 | 82 | /** 83 | * @return log level to write 84 | */ 85 | public Log4jLogLevel getLogLevel() { 86 | return logLevel; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/Log4jSlowQueryListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | 4 | import net.ttddyy.dsproxy.support.Log4jLogUtils; 5 | import org.apache.logging.log4j.LogManager; 6 | import org.apache.logging.log4j.Logger; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * Log slow query using Apache Commons Logging log4j. 12 | * 13 | * @author Ivan Jose Sanchez Pagador 14 | * @since 1.8 15 | */ 16 | public class Log4jSlowQueryListener extends AbstractSlowQueryLoggingListener { 17 | 18 | protected Logger logger = LogManager.getLogger(Log4jSlowQueryListener.class); 19 | protected Log4jLogLevel logLevel = Log4jLogLevel.WARN; // default WARN 20 | 21 | public Log4jSlowQueryListener() { 22 | } 23 | 24 | public Log4jSlowQueryListener(long threshold, TimeUnit thresholdTimeUnit) { 25 | this.threshold = threshold; 26 | this.thresholdTimeUnit = thresholdTimeUnit; 27 | } 28 | 29 | @Override 30 | protected void writeLog(String message) { 31 | Log4jLogUtils.writeLog(logger, this.logLevel, message); 32 | } 33 | 34 | public void setLogger(String loggerName) { 35 | setLogger(LogManager.getLogger(loggerName)); 36 | } 37 | 38 | public void setLogger(Logger logger) { 39 | this.logger = logger; 40 | } 41 | 42 | public void setLogLevel(Log4jLogLevel logLevel) { 43 | this.logLevel = logLevel; 44 | } 45 | 46 | public Logger getLogger() { 47 | return logger; 48 | } 49 | 50 | public Log4jLogLevel getLogLevel() { 51 | return logLevel; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/LoggingCondition.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | /** 4 | * Strategy to decide whether to perform logging logic. 5 | * 6 | * TODO: replace with BooleanSupplier once it moves java8 7 | * 8 | * @author Tadaya Tsuyukubo 9 | * @see AbstractQueryLoggingListener 10 | * @since 1.4.3 11 | */ 12 | public interface LoggingCondition { 13 | 14 | boolean getAsBoolean(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/OutputParameterLogEntryCreator.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.QueryInfo; 5 | import net.ttddyy.dsproxy.proxy.ParameterSetOperation; 6 | 7 | import java.sql.CallableStatement; 8 | import java.sql.SQLException; 9 | import java.util.List; 10 | 11 | /** 12 | * In addition to {@link DefaultQueryLogEntryCreator}, append output parameter values to the log for {@link CallableStatement}. 13 | * 14 | * @author Parikshit Navgire (navgire@optymyze.com) 15 | * @author Tadaya Tsuyukubo 16 | * @since 1.3.2 17 | */ 18 | public class OutputParameterLogEntryCreator extends DefaultQueryLogEntryCreator { 19 | 20 | @Override 21 | public String getLogEntry(ExecutionInfo execInfo, List queryInfoList, boolean writeDataSourceName, boolean writeConnectionId, boolean writeIsolation) { 22 | final StringBuilder sb = new StringBuilder(); 23 | sb.append(super.getLogEntry(execInfo, queryInfoList, writeDataSourceName, writeConnectionId, writeIsolation)); 24 | 25 | sb.append(", OutParams:["); 26 | 27 | for (QueryInfo queryInfo : queryInfoList) { 28 | for (List parameters : queryInfo.getParametersList()) { 29 | sb.append("("); 30 | if (hasOutputParameters(parameters)) { 31 | String str = getOutputParameters(parameters, (CallableStatement) execInfo.getStatement()); 32 | sb.append(str); 33 | } 34 | sb.append("),"); 35 | } 36 | } 37 | 38 | chompIfEndWith(sb, ','); 39 | sb.append("]"); 40 | return sb.toString(); 41 | } 42 | 43 | 44 | private String getOutputParameters(List params, CallableStatement st) { 45 | 46 | StringBuilder sb = new StringBuilder(); 47 | for (ParameterSetOperation param : params) { 48 | if (!ParameterSetOperation.isRegisterOutParameterOperation(param)) { 49 | continue; 50 | } 51 | 52 | Object key = param.getArgs()[0]; 53 | Object value = getOutputValueForDisplay(key, st); 54 | 55 | sb.append(key); 56 | sb.append("="); 57 | sb.append(value); 58 | sb.append(","); 59 | 60 | } 61 | chompIfEndWith(sb, ','); 62 | 63 | return sb.toString(); 64 | } 65 | 66 | protected Object getOutputValueForDisplay(Object key, CallableStatement cs) { 67 | Object value; 68 | try { 69 | if (key instanceof String) { 70 | value = cs.getObject((String) key); // access by name 71 | } else { 72 | value = cs.getObject((Integer) key); // access by index 73 | } 74 | } catch (SQLException e) { 75 | return "[FAILED TO RETRIEVE]"; 76 | } 77 | return value; 78 | } 79 | 80 | private boolean hasOutputParameters(List params) { 81 | for (ParameterSetOperation param : params) { 82 | if (ParameterSetOperation.isRegisterOutParameterOperation(param)) { 83 | return true; 84 | } 85 | } 86 | return false; 87 | } 88 | 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/ParameterValueConverter.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.proxy.ParameterSetOperation; 4 | 5 | import java.lang.reflect.Field; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * Interface to convert {@link ParameterSetOperation} to {@link String}. 11 | * 12 | * Used to convert parameter value to display value. 13 | * 14 | * @author Tadaya Tsuyukubo 15 | * @see RegisterOutParameterValueConverter 16 | * @see SetNullParameterValueConverter 17 | * @since 1.4 18 | */ 19 | public interface ParameterValueConverter { 20 | 21 | // key: int(code) specified in java.sql.Types, value: corresponding field name 22 | static final Map SQL_TYPENAME_BY_CODE = new HashMap() { 23 | { 24 | try { 25 | Class clazz = java.sql.Types.class; 26 | for (Field field : clazz.getFields()) { 27 | String name = field.getName(); 28 | int code = field.getInt(clazz); 29 | this.put(code, name.toUpperCase()); 30 | } 31 | } catch (Exception e) { 32 | } 33 | } 34 | }; 35 | 36 | 37 | String getValue(ParameterSetOperation param); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/QueryLogEntryCreator.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.QueryInfo; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Generate logging entry. 10 | * 11 | * @author Tadaya Tsuyukubo 12 | * @since 1.3 13 | */ 14 | public interface QueryLogEntryCreator { 15 | 16 | String getLogEntry(ExecutionInfo execInfo, List queryInfoList, boolean writeDataSourceName, boolean writeConnectionId, boolean writeIsolation); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/RegisterOutParameterValueConverter.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.proxy.ParameterSetOperation; 4 | 5 | /** 6 | * Converter for registerOutParameter parameter operations in {@link java.sql.CallableStatement}. 7 | * 8 | * @author Tadaya Tsuyukubo 9 | * @since 1.4 10 | */ 11 | public class RegisterOutParameterValueConverter implements ParameterValueConverter { 12 | 13 | @Override 14 | public String getValue(ParameterSetOperation param) { 15 | Object sqlType = param.getArgs()[1]; // 2nd arg is sqlType 16 | return this.getDisplayValue(sqlType); 17 | } 18 | 19 | public String getDisplayValue(Object sqlType) { 20 | StringBuilder sb = new StringBuilder(); 21 | sb.append("OUTPUT("); 22 | // for the second argument, it is either int or SQLType(in JDBC 4.2) 23 | if (sqlType instanceof Integer) { 24 | String sqlTypeName = ParameterValueConverter.SQL_TYPENAME_BY_CODE.get((Integer) sqlType); 25 | if (sqlTypeName != null) { 26 | sb.append(sqlTypeName); 27 | sb.append("["); 28 | sb.append(sqlType); 29 | sb.append("]"); 30 | } else { 31 | sb.append(sqlType); 32 | } 33 | } else { 34 | sb.append(sqlType); 35 | } 36 | 37 | sb.append(")"); 38 | return sb.toString(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/SLF4JLogLevel.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | /** 4 | * SLF4J log level representation. 5 | * 6 | * @author Tadaya Tsuyukubo 7 | */ 8 | public enum SLF4JLogLevel { 9 | // least serious to most serious 10 | TRACE, DEBUG, INFO, WARN, ERROR; 11 | 12 | public static SLF4JLogLevel nullSafeValueOf(String name) { 13 | if (name == null) { 14 | return null; 15 | } 16 | return valueOf(name); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/SLF4JQueryLoggingListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.support.SLF4JLogUtils; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * Log executed query information using SLF4J. 9 | * 10 | * @author Tadaya Tsuyukubo 11 | */ 12 | public class SLF4JQueryLoggingListener extends AbstractQueryLoggingListener { 13 | 14 | protected Logger logger = LoggerFactory.getLogger(SLF4JQueryLoggingListener.class); 15 | protected SLF4JLogLevel logLevel = SLF4JLogLevel.DEBUG; // default DEBUG 16 | 17 | public SLF4JQueryLoggingListener() { 18 | this.loggingCondition = new LoggingCondition() { 19 | @Override 20 | public boolean getAsBoolean() { 21 | switch (logLevel) { 22 | case TRACE: 23 | return logger.isTraceEnabled(); 24 | case DEBUG: 25 | return logger.isDebugEnabled(); 26 | case INFO: 27 | return logger.isInfoEnabled(); 28 | case WARN: 29 | return logger.isWarnEnabled(); 30 | case ERROR: 31 | return logger.isErrorEnabled(); 32 | } 33 | return false; 34 | } 35 | }; 36 | } 37 | 38 | @Override 39 | protected void writeLog(String message) { 40 | SLF4JLogUtils.writeLog(logger, this.logLevel, message); 41 | } 42 | 43 | public void setLogLevel(SLF4JLogLevel logLevel) { 44 | this.logLevel = logLevel; 45 | } 46 | 47 | /** 48 | * @deprecated use {{@link #setLogger(String)}} 49 | */ 50 | @Override 51 | @Deprecated 52 | protected void resetLogger(String loggerName) { 53 | this.logger = LoggerFactory.getLogger(loggerName); 54 | } 55 | 56 | /** 57 | * Override {@link Logger} instance that has specified logger name. 58 | * 59 | * @param loggerName logger name 60 | * @since 1.4.1 61 | */ 62 | public void setLogger(String loggerName) { 63 | this.logger = LoggerFactory.getLogger(loggerName); 64 | } 65 | 66 | /** 67 | * Override {@link Logger} instance. 68 | * 69 | * @param logger new logger instance 70 | * @since 1.4.1 71 | */ 72 | public void setLogger(Logger logger) { 73 | this.logger = logger; 74 | } 75 | 76 | /** 77 | * @return logger 78 | * @since 1.4.1 79 | */ 80 | public Logger getLogger() { 81 | return logger; 82 | } 83 | 84 | /** 85 | * @return log level to write 86 | * @since 1.4.1 87 | */ 88 | public SLF4JLogLevel getLogLevel() { 89 | return logLevel; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/SLF4JSlowQueryListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.support.SLF4JLogUtils; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | * Log slow query using SLF4j. 11 | * 12 | * @author Tadaya Tsuyukubo 13 | * @since 1.4.1 14 | */ 15 | public class SLF4JSlowQueryListener extends AbstractSlowQueryLoggingListener { 16 | 17 | protected Logger logger = LoggerFactory.getLogger(SLF4JSlowQueryListener.class); 18 | protected SLF4JLogLevel logLevel = SLF4JLogLevel.WARN; // default WARN 19 | 20 | public SLF4JSlowQueryListener() { 21 | } 22 | 23 | public SLF4JSlowQueryListener(long threshold, TimeUnit thresholdTimeUnit) { 24 | this.threshold = threshold; 25 | this.thresholdTimeUnit = thresholdTimeUnit; 26 | } 27 | 28 | @Override 29 | protected void writeLog(String message) { 30 | SLF4JLogUtils.writeLog(logger, this.logLevel, message); 31 | } 32 | 33 | public void setLogger(String loggerName) { 34 | setLogger(LoggerFactory.getLogger(loggerName)); 35 | } 36 | 37 | public void setLogger(Logger logger) { 38 | this.logger = logger; 39 | } 40 | 41 | public void setLogLevel(SLF4JLogLevel logLevel) { 42 | this.logLevel = logLevel; 43 | } 44 | 45 | public Logger getLogger() { 46 | return logger; 47 | } 48 | 49 | public SLF4JLogLevel getLogLevel() { 50 | return logLevel; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/SetNullParameterValueConverter.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.proxy.ParameterSetOperation; 4 | 5 | /** 6 | * Convert setNull parameter operation. 7 | * 8 | * @author Tadaya Tsuyukubo 9 | * @since 1.4 10 | */ 11 | public class SetNullParameterValueConverter implements ParameterValueConverter { 12 | 13 | @Override 14 | public String getValue(ParameterSetOperation param) { 15 | Integer sqlType = (Integer) param.getArgs()[1]; // second arg for setNull is always int 16 | return getDisplayValue(sqlType); 17 | } 18 | 19 | public String getDisplayValue(Integer sqlType) { 20 | 21 | String sqlTypeName = ParameterValueConverter.SQL_TYPENAME_BY_CODE.get(sqlType); 22 | 23 | StringBuilder sb = new StringBuilder(); 24 | sb.append("NULL"); 25 | if (sqlTypeName != null) { 26 | sb.append("(" + sqlTypeName + ")"); 27 | } 28 | return sb.toString(); 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/SystemOutQueryLoggingListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | /** 4 | * Output query logging to System.out. 5 | * 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.3 8 | */ 9 | public class SystemOutQueryLoggingListener extends AbstractQueryLoggingListener { 10 | 11 | public SystemOutQueryLoggingListener() { 12 | this.loggingCondition = new LoggingCondition() { 13 | @Override 14 | public boolean getAsBoolean() { 15 | return true; // always perform logging 16 | } 17 | }; 18 | } 19 | 20 | @Override 21 | protected void writeLog(String message) { 22 | System.out.println(message); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/listener/logging/SystemOutSlowQueryListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | /** 6 | * Log slow query to System.out. 7 | * 8 | * @author Tadaya Tsuyukubo 9 | * @since 1.4.1 10 | */ 11 | public class SystemOutSlowQueryListener extends AbstractSlowQueryLoggingListener { 12 | 13 | public SystemOutSlowQueryListener() { 14 | } 15 | 16 | public SystemOutSlowQueryListener(long threshold, TimeUnit thresholdTimeUnit) { 17 | this.threshold = threshold; 18 | this.thresholdTimeUnit = thresholdTimeUnit; 19 | } 20 | 21 | @Override 22 | protected void writeLog(String message) { 23 | System.out.println(message); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/CachedRowSetResultSetProxyLogicFactory.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | import net.ttddyy.dsproxy.DataSourceProxyException; 5 | import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; 6 | 7 | import javax.sql.rowset.CachedRowSet; 8 | import javax.sql.rowset.RowSetFactory; 9 | import javax.sql.rowset.RowSetProvider; 10 | import java.sql.ResultSet; 11 | import java.sql.SQLException; 12 | 13 | /** 14 | * Factory to create {@link CachedRowSetResultSetProxyLogic}. 15 | * 16 | * Provide a {@link ResultSet} proxy using {@link CachedRowSet} which provides disconnected scrollability. 17 | * 18 | * This class uses {@link RowSetFactory} that requires jdk1.7+ to create a {@link CachedRowSet}. 19 | * You could change the creation strategy either uses mechanism of {@link RowSetFactory} or simply extend this class 20 | * or implement another {@link ResultSetProxyLogicFactory}. 21 | * 22 | * The default {@link CachedRowSet} implementation is {@code com.sun.rowset.CachedRowSetImpl}. 23 | * 24 | * @author Tadaya Tsuyukubo 25 | * @see RowSetFactory 26 | * @see CachedRowSet 27 | * @see CachedRowSetResultSetProxyLogic 28 | * @since 1.4.7 29 | */ 30 | @IgnoreJRERequirement 31 | public class CachedRowSetResultSetProxyLogicFactory implements ResultSetProxyLogicFactory { 32 | 33 | private final RowSetFactory rowSetFactory; 34 | 35 | public CachedRowSetResultSetProxyLogicFactory() { 36 | try { 37 | this.rowSetFactory = RowSetProvider.newFactory(); 38 | } catch (SQLException ex) { 39 | throw new RuntimeException(ex); 40 | } 41 | } 42 | 43 | @Override 44 | public ResultSetProxyLogic create(ResultSet resultSet, ConnectionInfo connectionInfo, ProxyConfig proxyConfig) { 45 | ResultSet cachedRowSet = getCachedRowSet(resultSet); 46 | return new CachedRowSetResultSetProxyLogic(resultSet, cachedRowSet, connectionInfo, proxyConfig); 47 | } 48 | 49 | protected ResultSet getCachedRowSet(ResultSet resultSet) { 50 | try { 51 | // CachedRowSet only works with non-null ResultSet 52 | if (resultSet.getMetaData().getColumnCount() > 0) { 53 | CachedRowSet cachedRowSet = this.rowSetFactory.createCachedRowSet(); 54 | cachedRowSet.populate(resultSet); 55 | return cachedRowSet; 56 | } else { 57 | // for null result-set, return the given one 58 | return resultSet; 59 | } 60 | } catch (SQLException e) { 61 | throw new DataSourceProxyException("Failed to create CachedRowSet", e); 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/DataSourceNameAware.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | /** 4 | * @author Tadaya Tsuyukubo 5 | */ 6 | public interface DataSourceNameAware { 7 | String getDataSourceName(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/DataSourceProxyLogic.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ConnectionIdManager; 4 | import net.ttddyy.dsproxy.ConnectionInfo; 5 | import net.ttddyy.dsproxy.listener.MethodExecutionContext; 6 | 7 | import javax.sql.DataSource; 8 | import java.lang.reflect.Method; 9 | import java.sql.Connection; 10 | 11 | /** 12 | * Proxy Logic implementation for {@link DataSource} methods. 13 | * 14 | * @author Tadaya Tsuyukubo 15 | * @since 1.2 16 | */ 17 | public class DataSourceProxyLogic extends ProxyLogicSupport { 18 | 19 | private DataSource dataSource; 20 | private ProxyConfig proxyConfig; 21 | 22 | public DataSourceProxyLogic(DataSource dataSource, ProxyConfig proxyConfig) { 23 | this.dataSource = dataSource; 24 | this.proxyConfig = proxyConfig; 25 | } 26 | 27 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 28 | return proceedMethodExecution(this.proxyConfig, this.dataSource, null, proxy, method, args); 29 | } 30 | 31 | @Override 32 | protected Object performProxyLogic(Object proxy, Method method, Object[] args, MethodExecutionContext methodContext) throws Throwable { 33 | JdbcProxyFactory jdbcProxyFactory = this.proxyConfig.getJdbcProxyFactory(); 34 | ConnectionIdManager connectionIdManager = this.proxyConfig.getConnectionIdManager(); 35 | 36 | final String methodName = method.getName(); 37 | if (isCommonMethod(methodName)) { 38 | return handleCommonMethod(methodName, this.dataSource, this.proxyConfig, args); 39 | } 40 | 41 | final Object retVal = proceedExecution(method, this.dataSource, args); 42 | 43 | if ("getConnection".equals(methodName)) { 44 | Connection conn = (Connection) retVal; 45 | String connId = connectionIdManager.getId(conn); 46 | ConnectionInfo connectionInfo = new ConnectionInfo(); 47 | connectionInfo.setConnectionId(connId); 48 | connectionInfo.setDataSourceName(this.proxyConfig.getDataSourceName()); 49 | if (this.proxyConfig.isRetrieveIsolationLevel()) { 50 | connectionInfo.setIsolationLevel(conn.getTransactionIsolation()); 51 | } 52 | 53 | // make ConnectionInfo available in afterMethod() callback 54 | methodContext.setConnectionInfo(connectionInfo); 55 | 56 | return jdbcProxyFactory.createConnection((Connection) retVal, connectionInfo, this.proxyConfig); 57 | } 58 | 59 | return retVal; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/DefaultConnectionIdManager.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ConnectionIdManager; 4 | 5 | import java.sql.Connection; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | import java.util.concurrent.atomic.AtomicLong; 9 | 10 | /** 11 | * Default implementation of {@link ConnectionIdManager}. 12 | * 13 | * This implementation returns sequentially increasing unique number as connection id. 14 | * 15 | * @author Tadaya Tsuyukubo 16 | * @since 1.4.2 17 | */ 18 | public class DefaultConnectionIdManager implements ConnectionIdManager { 19 | 20 | private AtomicLong idCounter = new AtomicLong(0); 21 | 22 | private Set openIds = new HashSet(); 23 | 24 | @Override 25 | public String getId(Connection connection) { 26 | String id = String.valueOf(this.idCounter.incrementAndGet()); 27 | synchronized (this) { 28 | this.openIds.add(id); 29 | } 30 | return id; 31 | } 32 | 33 | @Override 34 | public void addClosedId(String closedId) { 35 | synchronized (this) { 36 | this.openIds.remove(closedId); 37 | } 38 | } 39 | 40 | @Override 41 | public Set getOpenConnectionIds() { 42 | return new HashSet(this.openIds); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/GlobalConnectionIdManager.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ConnectionIdManager; 4 | 5 | import java.sql.Connection; 6 | import java.util.Collections; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.concurrent.atomic.AtomicLong; 10 | 11 | /** 12 | * {@link ConnectionIdManager} implementation that emits connection IDs(sequential number) unique across all 13 | * datasources in JVM. 14 | * 15 | * @author Tadaya Tsuyukubo 16 | * @since 1.6 17 | */ 18 | public class GlobalConnectionIdManager implements ConnectionIdManager { 19 | 20 | private static AtomicLong ID_COUNTER = new AtomicLong(0); 21 | 22 | private Set openIds = Collections.synchronizedSet(new HashSet()); 23 | 24 | /** 25 | * Reset internal id counter. 26 | */ 27 | public static void resetId() { 28 | ID_COUNTER.set(0); 29 | } 30 | 31 | @Override 32 | public String getId(Connection connection) { 33 | String id = String.valueOf(ID_COUNTER.incrementAndGet()); 34 | this.openIds.add(id); 35 | return id; 36 | } 37 | 38 | @Override 39 | public void addClosedId(String closedId) { 40 | this.openIds.remove(closedId); 41 | } 42 | 43 | @Override 44 | public Set getOpenConnectionIds() { 45 | return Collections.unmodifiableSet(this.openIds); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/JdbcProxyFactory.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | import net.ttddyy.dsproxy.proxy.jdk.JdkJdbcProxyFactory; 5 | 6 | import javax.sql.DataSource; 7 | import java.sql.CallableStatement; 8 | import java.sql.Connection; 9 | import java.sql.PreparedStatement; 10 | import java.sql.ResultSet; 11 | import java.sql.Statement; 12 | 13 | /** 14 | * Factory interface to return a proxy with InvocationHandler used by datasource-proxy. 15 | * 16 | * @author Tadaya Tsuyukubo 17 | */ 18 | public interface JdbcProxyFactory { 19 | 20 | /** 21 | * use JDK proxy as default. 22 | */ 23 | JdbcProxyFactory DEFAULT = new JdkJdbcProxyFactory(); 24 | 25 | 26 | DataSource createDataSource(DataSource dataSource, ProxyConfig proxyConfig); 27 | 28 | Connection createConnection(Connection connection, ConnectionInfo connectionInfo, ProxyConfig proxyConfig); 29 | 30 | Statement createStatement(Statement statement, ConnectionInfo connectionInfo, Connection proxyConnection, 31 | ProxyConfig proxyConfig); 32 | 33 | PreparedStatement createPreparedStatement(PreparedStatement preparedStatement, String query, 34 | ConnectionInfo connectionInfo, Connection proxyConnection, 35 | ProxyConfig proxyConfig, boolean generateKey); 36 | 37 | CallableStatement createCallableStatement(CallableStatement callableStatement, String query, 38 | ConnectionInfo connectionInfo, 39 | Connection proxyConnection, ProxyConfig proxyConfig); 40 | 41 | /** 42 | * Create a proxy for {@link ResultSet}. 43 | * 44 | * @since 1.4.3 45 | */ 46 | ResultSet createResultSet(ResultSet resultSet, ConnectionInfo connectionInfo, ProxyConfig proxyConfig); 47 | 48 | /** 49 | * Create a proxy for {@link ResultSet} generated keys. 50 | * 51 | * @since 1.4.5 52 | */ 53 | ResultSet createGeneratedKeys(ResultSet resultSet, ConnectionInfo connectionInfo, ProxyConfig proxyConfig); 54 | 55 | } -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/NanoTimeStopwatchFactory.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | /** 4 | * Factory to create {@link NanoTimeStopwatch}. 5 | * 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.5.1 8 | */ 9 | public class NanoTimeStopwatchFactory implements StopwatchFactory { 10 | 11 | @Override 12 | public Stopwatch create() { 13 | return new NanoTimeStopwatch(); 14 | } 15 | 16 | /** 17 | * {@link Stopwatch} implementation that uses {@code System.nanoTime()}. 18 | * 19 | * {@link #getElapsedTime()} returns nano seconds. 20 | */ 21 | public static class NanoTimeStopwatch implements Stopwatch { 22 | 23 | private long startTime; 24 | 25 | @Override 26 | public Stopwatch start() { 27 | this.startTime = System.nanoTime(); 28 | return this; 29 | } 30 | 31 | /** 32 | * Elapsed nano seconds from {@link #start()}. 33 | * 34 | * @return nano second from {@link #start()} 35 | */ 36 | @Override 37 | public long getElapsedTime() { 38 | return System.nanoTime() - this.startTime; 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/NativeJdbcExtractUtils.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import java.sql.CallableStatement; 4 | import java.sql.Connection; 5 | import java.sql.PreparedStatement; 6 | import java.sql.Statement; 7 | 8 | /** 9 | * @author Tadaya Tsuyukubo 10 | */ 11 | public class NativeJdbcExtractUtils { 12 | public static Connection getConnection(Connection connection) { 13 | return getNativeJdbcObject(connection); 14 | } 15 | 16 | public static Statement getStatement(Statement statement) { 17 | return getNativeJdbcObject(statement); 18 | } 19 | 20 | public static PreparedStatement getPreparedStatement(PreparedStatement preparedStatement) { 21 | return getNativeJdbcObject(preparedStatement); 22 | } 23 | 24 | public static CallableStatement getCallableStatement(CallableStatement callableStatement) { 25 | return getNativeJdbcObject(callableStatement); 26 | } 27 | 28 | @SuppressWarnings("unchecked") 29 | private static T getNativeJdbcObject(T obj) { 30 | T objToUse = obj; 31 | while (objToUse instanceof ProxyJdbcObject) { 32 | objToUse = (T) ((ProxyJdbcObject) objToUse).getTarget(); 33 | } 34 | return objToUse; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/ObjectArrayUtils.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | /** 4 | * @author Tadaya Tsuyukubo 5 | */ 6 | public class ObjectArrayUtils { 7 | public static boolean isFirstArgString(Object[] args) { 8 | return args != null && args.length >= 1 && args[0] instanceof String; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/ParameterKeyUtils.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author Tadaya Tsuyukubo 8 | * @since 1.4 9 | */ 10 | public class ParameterKeyUtils { 11 | public static Map filterBy(Map map, ParameterKey.ParameterKeyType type) { 12 | Map result = new LinkedHashMap(); 13 | for (Map.Entry entry : map.entrySet()) { 14 | ParameterKey key = entry.getKey(); 15 | if (key.getType() == type) { 16 | result.put(key, entry.getValue()); 17 | } 18 | } 19 | return result; 20 | } 21 | 22 | public static Map toNameMap(Map map) { 23 | Map filtered = filterBy(map, ParameterKey.ParameterKeyType.BY_NAME); 24 | Map result = new LinkedHashMap(); 25 | for (Map.Entry entry : filtered.entrySet()) { 26 | result.put(entry.getKey().getName(), entry.getValue()); 27 | } 28 | return result; 29 | } 30 | 31 | public static Map toIndexMap(Map map) { 32 | Map filtered = filterBy(map, ParameterKey.ParameterKeyType.BY_INDEX); 33 | Map result = new LinkedHashMap(); 34 | for (Map.Entry entry : filtered.entrySet()) { 35 | result.put(entry.getKey().getIndex(), entry.getValue()); 36 | } 37 | return result; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/ParameterSetOperation.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * Keeps a method and its arguments when parameter-set-method is called. 7 | * 8 | * @author Tadaya Tsuyukubo 9 | * @see net.ttddyy.dsproxy.proxy.jdk.PreparedStatementInvocationHandler 10 | * @since 1.2 11 | */ 12 | public class ParameterSetOperation { 13 | 14 | /** 15 | * Check the given operation is {@link java.sql.CallableStatement#registerOutParameter} method by method name. 16 | * 17 | * @param operation a parameter set operation 18 | * @return true if it is a {@code registerOutParameter} operation 19 | * @since 1.4 20 | */ 21 | public static boolean isRegisterOutParameterOperation(ParameterSetOperation operation) { 22 | String methodName = operation.getMethod().getName(); 23 | return StatementMethodNames.PARAMETER_METHOD_REGISTER_OUT_PARAMETER.equals(methodName); 24 | } 25 | 26 | /** 27 | * Check the given operation is {@code setNull} method by method name. 28 | * 29 | * @param operation a parameter set operation 30 | * @return true if it is a {@code setNull} operation 31 | * @since 1.4 32 | */ 33 | public static boolean isSetNullParameterOperation(ParameterSetOperation operation) { 34 | String methodName = operation.getMethod().getName(); 35 | return StatementMethodNames.PARAMETER_METHOD_SET_NULL.equals(methodName); 36 | } 37 | 38 | private Method method; 39 | private Object[] args; 40 | 41 | public ParameterSetOperation() { 42 | } 43 | 44 | public ParameterSetOperation(Method method, Object[] args) { 45 | this.method = method; 46 | this.args = args; 47 | } 48 | 49 | 50 | public Method getMethod() { 51 | return method; 52 | } 53 | 54 | public void setMethod(Method method) { 55 | this.method = method; 56 | } 57 | 58 | public Object[] getArgs() { 59 | return args; 60 | } 61 | 62 | public void setArgs(Object[] args) { 63 | this.args = args; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/ProxyJdbcObject.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | /** 4 | * Provide a method to unwrap the original jdbc object from proxy object. 5 | * 6 | *

Proxy object created by {@link JdbcProxyFactory} implements this interface. 7 | * 8 | * @author Tadaya Tsuyukubo 9 | * @see JdbcProxyFactory 10 | * @see net.ttddyy.dsproxy.proxy.jdk.ConnectionInvocationHandler 11 | * @see net.ttddyy.dsproxy.proxy.jdk.StatementInvocationHandler 12 | * @see net.ttddyy.dsproxy.proxy.jdk.PreparedStatementInvocationHandler 13 | * @see net.ttddyy.dsproxy.proxy.jdk.CallableStatementInvocationHandler 14 | * @see net.ttddyy.dsproxy.proxy.jdk.ResultSetInvocationHandler 15 | */ 16 | public interface ProxyJdbcObject { 17 | 18 | /** 19 | * Method to return wrapped source object(Connection, Statement, PreparedStatement, CallableStatement). 20 | * 21 | * @return source object 22 | */ 23 | Object getTarget(); 24 | 25 | /** 26 | * Retrieve an associated {@link ProxyConfig}. 27 | * @return an associated {@link ProxyConfig} 28 | * @since 1.10 29 | */ 30 | ProxyConfig getProxyConfig(); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/ReflectionUtils.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.4.6 8 | */ 9 | public class ReflectionUtils { 10 | 11 | public static Method getMethodIfAvailable(Class clazz, String name, Class... parameterTypes) { 12 | try { 13 | return clazz.getMethod(name, parameterTypes); 14 | } catch (NoSuchMethodException e) { 15 | return null; 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/RepeatableReadResultSetProxyLogicFactory.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | import net.ttddyy.dsproxy.DataSourceProxyException; 5 | 6 | import java.sql.ResultSet; 7 | import java.sql.ResultSetMetaData; 8 | import java.sql.SQLException; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * Factory to create {@link RepeatableReadResultSetProxyLogic}. 14 | * 15 | * @author Tadaya Tsuyukubo 16 | * @author Liam Williams 17 | * @since 1.4.3 18 | */ 19 | public class RepeatableReadResultSetProxyLogicFactory implements ResultSetProxyLogicFactory { 20 | 21 | @Override 22 | public ResultSetProxyLogic create(ResultSet resultSet, ConnectionInfo connectionInfo, ProxyConfig proxyConfig) { 23 | Map columnNameToIndex = columnNameToIndex(resultSet); 24 | return RepeatableReadResultSetProxyLogic.Builder.create() 25 | .resultSet(resultSet) 26 | .connectionInfo(connectionInfo) 27 | .proxyConfig(proxyConfig) 28 | .columnNameToIndex(columnNameToIndex) 29 | .columnCount(columnNameToIndex.size()) 30 | .build(); 31 | } 32 | 33 | private Map columnNameToIndex(ResultSet resultSet) { 34 | try { 35 | ResultSetMetaData metaData = resultSet.getMetaData(); 36 | int columnCount = metaData.getColumnCount(); 37 | Map columnNameToIndex = new HashMap(); 38 | for (int i = 1; i <= columnCount; i++) { 39 | columnNameToIndex.put(metaData.getColumnLabel(i).toUpperCase(), i); 40 | } 41 | return columnNameToIndex; 42 | } catch (SQLException e) { 43 | throw new DataSourceProxyException("Failed to obtain resultset metadata", e); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/ResultSetProxyLogic.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * Proxy logic for {@link java.sql.ResultSet}. 7 | * 8 | * @author Tadaya Tsuyukubo 9 | * @since 1.4.3 10 | */ 11 | public interface ResultSetProxyLogic { 12 | 13 | Object invoke(Object proxy, Method method, Object[] args) throws Throwable; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/ResultSetProxyLogicFactory.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | 5 | import java.sql.ResultSet; 6 | 7 | /** 8 | * Factory to create {@link ResultSetProxyLogic}. 9 | * 10 | * @author Tadaya Tsuyukubo 11 | * @since 1.4.3 12 | */ 13 | public interface ResultSetProxyLogicFactory { 14 | 15 | ResultSetProxyLogicFactory DEFAULT = new SimpleResultSetProxyLogicFactory(); 16 | 17 | ResultSetProxyLogic create(ResultSet resultSet, ConnectionInfo connectionInfo, ProxyConfig proxyConfig); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/SimpleResultSetProxyLogic.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | import net.ttddyy.dsproxy.listener.MethodExecutionContext; 5 | 6 | import java.lang.reflect.Method; 7 | import java.sql.ResultSet; 8 | 9 | /** 10 | * Simply delegate method calls to the actual {@link ResultSet}. 11 | * 12 | * @author Tadaya Tsuyukubo 13 | * @since 1.4.3 14 | */ 15 | public class SimpleResultSetProxyLogic extends ProxyLogicSupport implements ResultSetProxyLogic { 16 | 17 | private ResultSet resultSet; 18 | private ConnectionInfo connectionInfo; 19 | private ProxyConfig proxyConfig; 20 | 21 | public SimpleResultSetProxyLogic(ResultSet resultSet, ConnectionInfo connectionInfo, ProxyConfig proxyConfig) { 22 | this.resultSet = resultSet; 23 | this.connectionInfo = connectionInfo; 24 | this.proxyConfig = proxyConfig; 25 | } 26 | 27 | @Override 28 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 29 | return proceedMethodExecution(this.proxyConfig, this.resultSet, this.connectionInfo, proxy, method, args); 30 | } 31 | 32 | @Override 33 | protected Object performProxyLogic(Object proxy, Method method, Object[] args, MethodExecutionContext methodContext) throws Throwable { 34 | final String methodName = method.getName(); 35 | if (isCommonMethod(methodName)) { 36 | return handleCommonMethod(methodName, this.resultSet, this.proxyConfig, args); 37 | } 38 | return proceedExecution(method, this.resultSet, args); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/SimpleResultSetProxyLogicFactory.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | 5 | import java.sql.ResultSet; 6 | 7 | /** 8 | * Factory to create {@link SimpleResultSetProxyLogic}. 9 | * 10 | * @author Tadaya Tsuyukubo 11 | * @since 1.4.3 12 | */ 13 | public class SimpleResultSetProxyLogicFactory implements ResultSetProxyLogicFactory { 14 | 15 | @Override 16 | public ResultSetProxyLogic create(ResultSet resultSet, ConnectionInfo connectionInfo, ProxyConfig proxyConfig) { 17 | return new SimpleResultSetProxyLogic(resultSet, connectionInfo, proxyConfig); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/Stopwatch.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.listener.MethodExecutionContext; 5 | 6 | /** 7 | * Used to measure elapsed time for execution. 8 | * 9 | * @author Tadaya Tsuyukubo 10 | * @see SystemStopwatchFactory.SystemStopwatch 11 | * @see NanoTimeStopwatchFactory.NanoTimeStopwatch 12 | * @see ExecutionInfo#getElapsedTime() 13 | * @see MethodExecutionContext#getElapsedTime() 14 | * @since 1.5.1 15 | */ 16 | public interface Stopwatch { 17 | 18 | /** 19 | * Start the stopwatch. 20 | * 21 | * @return stopwatch 22 | */ 23 | Stopwatch start(); 24 | 25 | /** 26 | * Get the time from {@link #start()}. 27 | * The unit of returned time depends on the implementation class. 28 | * 29 | * @return elapsed time 30 | */ 31 | long getElapsedTime(); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/StopwatchFactory.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | /** 4 | * Factory to create {@link Stopwatch}. 5 | * 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.5.1 8 | */ 9 | public interface StopwatchFactory { 10 | 11 | Stopwatch create(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/SystemStopwatchFactory.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | /** 6 | * Factory to create {@link SystemStopwatch} which uses monotonic time. 7 | *

The unit of time is milliseconds. 8 | * 9 | * @author Tadaya Tsuyukubo 10 | * @since 1.5.1 11 | */ 12 | public class SystemStopwatchFactory implements StopwatchFactory { 13 | 14 | @Override 15 | public Stopwatch create() { 16 | return new SystemStopwatch(); 17 | } 18 | 19 | /** 20 | * Uses monotonic time to calculate elapsed time. 21 | *

The unit of time is milliseconds. 22 | */ 23 | public static class SystemStopwatch implements Stopwatch { 24 | 25 | private long startTime; 26 | 27 | @Override 28 | public Stopwatch start() { 29 | this.startTime = System.nanoTime(); 30 | return this; 31 | } 32 | 33 | /** 34 | * Elapsed milliseconds from {@link #start()}. 35 | * 36 | * @return millisecond from {@link #start()} 37 | */ 38 | @Override 39 | public long getElapsedTime() { 40 | return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - this.startTime); 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/jdk/CallableStatementInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy.jdk; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | import net.ttddyy.dsproxy.StatementType; 5 | import net.ttddyy.dsproxy.proxy.ProxyConfig; 6 | import net.ttddyy.dsproxy.proxy.StatementProxyLogic; 7 | 8 | import java.lang.reflect.InvocationHandler; 9 | import java.lang.reflect.Method; 10 | import java.sql.CallableStatement; 11 | import java.sql.Connection; 12 | 13 | /** 14 | * Proxy InvocationHandler for {@link java.sql.CallableStatement}. 15 | * 16 | * @author Tadaya Tsuyukubo 17 | */ 18 | public class CallableStatementInvocationHandler implements InvocationHandler { 19 | 20 | private StatementProxyLogic delegate; 21 | 22 | public CallableStatementInvocationHandler( 23 | CallableStatement cs, String query, ConnectionInfo connectionInfo, 24 | Connection proxyConnection, ProxyConfig proxyConfig) { 25 | 26 | this.delegate = StatementProxyLogic.Builder.create() 27 | .statement(cs, StatementType.CALLABLE) 28 | .query(query) 29 | .connectionInfo(connectionInfo) 30 | .proxyConnection(proxyConnection) 31 | .proxyConfig(proxyConfig) 32 | .build(); 33 | } 34 | 35 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 36 | return delegate.invoke(proxy, method, args); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/jdk/ConnectionInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy.jdk; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | import net.ttddyy.dsproxy.proxy.ConnectionProxyLogic; 5 | import net.ttddyy.dsproxy.proxy.ProxyConfig; 6 | 7 | import java.lang.reflect.InvocationHandler; 8 | import java.lang.reflect.Method; 9 | import java.sql.Connection; 10 | 11 | /** 12 | * Proxy InvocationHandler for {@link java.sql.Connection}. 13 | * 14 | * @author Tadaya Tsuyukubo 15 | */ 16 | public class ConnectionInvocationHandler implements InvocationHandler { 17 | 18 | private ConnectionProxyLogic delegate; 19 | 20 | public ConnectionInvocationHandler(Connection connection, ConnectionInfo connectionInfo, ProxyConfig proxyConfig) { 21 | this.delegate = new ConnectionProxyLogic(connection, connectionInfo, proxyConfig); 22 | } 23 | 24 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 25 | return delegate.invoke(proxy, method, args); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/jdk/DataSourceInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy.jdk; 2 | 3 | import net.ttddyy.dsproxy.proxy.DataSourceProxyLogic; 4 | import net.ttddyy.dsproxy.proxy.ProxyConfig; 5 | 6 | import javax.sql.DataSource; 7 | import java.lang.reflect.InvocationHandler; 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * Proxy InvocationHandler for {@link javax.sql.DataSource}. 12 | * 13 | * @author Tadaya Tsuyukubo 14 | */ 15 | public class DataSourceInvocationHandler implements InvocationHandler { 16 | 17 | private DataSourceProxyLogic delegate; 18 | 19 | public DataSourceInvocationHandler(DataSource dataSource, ProxyConfig proxyConfig) { 20 | delegate = new DataSourceProxyLogic(dataSource, proxyConfig); 21 | } 22 | 23 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 24 | return delegate.invoke(proxy, method, args); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/jdk/PreparedStatementInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy.jdk; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | import net.ttddyy.dsproxy.StatementType; 5 | import net.ttddyy.dsproxy.proxy.StatementProxyLogic; 6 | import net.ttddyy.dsproxy.proxy.ProxyConfig; 7 | 8 | import java.lang.reflect.InvocationHandler; 9 | import java.lang.reflect.Method; 10 | import java.sql.Connection; 11 | import java.sql.PreparedStatement; 12 | 13 | /** 14 | * Proxy InvocationHandler for {@link java.sql.PreparedStatement}. 15 | * 16 | * @author Tadaya Tsuyukubo 17 | */ 18 | public class PreparedStatementInvocationHandler implements InvocationHandler { 19 | 20 | private StatementProxyLogic delegate; 21 | 22 | public PreparedStatementInvocationHandler( 23 | PreparedStatement ps, String query, ConnectionInfo connectionInfo, 24 | Connection proxyConnection, ProxyConfig proxyConfig, boolean generateKey) { 25 | 26 | this.delegate = StatementProxyLogic.Builder.create() 27 | .statement(ps, StatementType.PREPARED) 28 | .query(query) 29 | .connectionInfo(connectionInfo) 30 | .proxyConnection(proxyConnection) 31 | .proxyConfig(proxyConfig) 32 | .generateKey(generateKey) 33 | .build(); 34 | } 35 | 36 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 37 | return delegate.invoke(proxy, method, args); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/jdk/ResultSetInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy.jdk; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | import net.ttddyy.dsproxy.proxy.ProxyConfig; 5 | import net.ttddyy.dsproxy.proxy.ResultSetProxyLogic; 6 | import net.ttddyy.dsproxy.proxy.ResultSetProxyLogicFactory; 7 | 8 | import java.lang.reflect.InvocationHandler; 9 | import java.lang.reflect.Method; 10 | import java.sql.ResultSet; 11 | 12 | /** 13 | * Proxy InvocationHandler for {@link java.sql.ResultSet}. 14 | * 15 | * @author Liam Williams 16 | * @author Tadaya Tsuyukubo 17 | * @since 1.4 18 | */ 19 | public class ResultSetInvocationHandler implements InvocationHandler { 20 | 21 | private ResultSetProxyLogic delegate; 22 | 23 | public ResultSetInvocationHandler(ResultSetProxyLogicFactory factory, ResultSet resultSet, ConnectionInfo connectionInfo, ProxyConfig proxyConfig) { 24 | this.delegate = factory.create(resultSet, connectionInfo, proxyConfig); 25 | } 26 | 27 | @Override 28 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 29 | return delegate.invoke(proxy, method, args); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/proxy/jdk/StatementInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy.jdk; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | import net.ttddyy.dsproxy.StatementType; 5 | import net.ttddyy.dsproxy.proxy.StatementProxyLogic; 6 | import net.ttddyy.dsproxy.proxy.ProxyConfig; 7 | 8 | import java.lang.reflect.InvocationHandler; 9 | import java.lang.reflect.Method; 10 | import java.sql.Connection; 11 | import java.sql.Statement; 12 | 13 | /** 14 | * Proxy InvocationHandler for {@link java.sql.Statement}. 15 | * 16 | * @author Tadaya Tsuyukubo 17 | */ 18 | public class StatementInvocationHandler implements InvocationHandler { 19 | 20 | private StatementProxyLogic delegate; 21 | 22 | public StatementInvocationHandler( 23 | Statement stmt, ConnectionInfo connectionInfo, 24 | Connection proxyConnection, ProxyConfig proxyConfig) { 25 | this.delegate = StatementProxyLogic.Builder.create() 26 | .statement(stmt, StatementType.STATEMENT) 27 | .connectionInfo(connectionInfo) 28 | .proxyConnection(proxyConnection) 29 | .proxyConfig(proxyConfig) 30 | .build(); 31 | } 32 | 33 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 34 | return delegate.invoke(proxy, method, args); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/AbstractQueryCountLoggingHandlerInterceptor.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.QueryCount; 4 | import net.ttddyy.dsproxy.QueryCountHolder; 5 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | /** 13 | * @author Tadaya Tsuyukubo 14 | * @since 1.3 15 | */ 16 | public abstract class AbstractQueryCountLoggingHandlerInterceptor extends HandlerInterceptorAdapter { 17 | 18 | private boolean clearQueryCounter = true; 19 | private boolean writeAsJson = false; 20 | private QueryCountLogEntryCreator logFormatter = new DefaultQueryCountLogEntryCreator(); 21 | 22 | @Override 23 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 24 | 25 | List dsNames = QueryCountHolder.getDataSourceNamesAsList(); 26 | Collections.sort(dsNames); 27 | 28 | for (String dsName : dsNames) { 29 | QueryCount count = QueryCountHolder.get(dsName); 30 | String logEntry; 31 | if (this.writeAsJson) { 32 | logEntry = logFormatter.getLogMessageAsJson(dsName, count); 33 | } else { 34 | logEntry = logFormatter.getLogMessage(dsName, count); 35 | } 36 | writeLog(logEntry); 37 | } 38 | 39 | if (clearQueryCounter) { 40 | QueryCountHolder.clear(); 41 | } 42 | 43 | } 44 | 45 | protected abstract void writeLog(String logEntry); 46 | 47 | /** 48 | * Specify logger name. 49 | * 50 | * @param loggerName logger name 51 | * @since 1.4.1 52 | */ 53 | public void setLoggerName(String loggerName) { 54 | resetLogger(loggerName); 55 | } 56 | 57 | /** 58 | * Callback method to reset the logger object in concrete class when log name is specified. 59 | * 60 | * @param loggerName logger name 61 | * @since 1.4.1 62 | */ 63 | protected void resetLogger(String loggerName) { 64 | } 65 | 66 | public void setClearQueryCounter(boolean clearQueryCounter) { 67 | this.clearQueryCounter = clearQueryCounter; 68 | } 69 | 70 | public void setLogFormatter(QueryCountLogEntryCreator logFormatter) { 71 | this.logFormatter = logFormatter; 72 | } 73 | 74 | public void setWriteAsJson(boolean writeAsJson) { 75 | this.writeAsJson = writeAsJson; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/AbstractQueryCountLoggingRequestListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.QueryCount; 4 | import net.ttddyy.dsproxy.QueryCountHolder; 5 | 6 | import javax.servlet.ServletRequestEvent; 7 | import javax.servlet.ServletRequestListener; 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | /** 12 | * @author Tadaya Tsuyukubo 13 | * @since 1.3 14 | */ 15 | public abstract class AbstractQueryCountLoggingRequestListener implements ServletRequestListener { 16 | private QueryCountLogEntryCreator logFormatter = new DefaultQueryCountLogEntryCreator(); 17 | private boolean writeAsJson = false; 18 | 19 | @Override 20 | public void requestInitialized(ServletRequestEvent servletRequestEvent) { 21 | // No-op 22 | } 23 | 24 | @Override 25 | public void requestDestroyed(ServletRequestEvent sre) { 26 | 27 | List dsNames = QueryCountHolder.getDataSourceNamesAsList(); 28 | Collections.sort(dsNames); 29 | 30 | for (String dsName : dsNames) { 31 | QueryCount count = QueryCountHolder.get(dsName); 32 | String logEntry; 33 | if (this.writeAsJson) { 34 | logEntry = logFormatter.getLogMessageAsJson(dsName, count); 35 | } else { 36 | logEntry = logFormatter.getLogMessage(dsName, count); 37 | } 38 | writeLog(sre, logEntry); 39 | } 40 | 41 | QueryCountHolder.clear(); 42 | } 43 | 44 | protected abstract void writeLog(ServletRequestEvent servletRequestEvent, String logEntry); 45 | 46 | public void setLogFormatter(QueryCountLogEntryCreator logFormatter) { 47 | this.logFormatter = logFormatter; 48 | } 49 | 50 | public void setWriteAsJson(boolean writeAsJson) { 51 | this.writeAsJson = writeAsJson; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/BeanNameProxyDataSource.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.proxy.ProxyConfig; 4 | import org.springframework.beans.factory.BeanNameAware; 5 | 6 | /** 7 | * Extending {@link ProxyDataSource} to use 8 | * spring bean name(id) as dataSourceName when it is not set. 9 | * 10 | * @author Tadaya Tsuyukubo 11 | */ 12 | public class BeanNameProxyDataSource extends ProxyDataSource implements BeanNameAware { 13 | 14 | public void setBeanName(String name) { 15 | final String dataSourceName = getProxyConfig().getDataSourceName(); 16 | if (dataSourceName == null || "".equals(dataSourceName)) { 17 | ProxyConfig proxyConfig = ProxyConfig.Builder.from(getProxyConfig()).dataSourceName(name).build(); 18 | setProxyConfig(proxyConfig); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/CommonsLogUtils.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.CommonsLogLevel; 4 | import org.apache.commons.logging.Log; 5 | 6 | /** 7 | * @author Tadaya Tsuyukubo 8 | */ 9 | public class CommonsLogUtils { 10 | 11 | public static void writeLog(Log log, CommonsLogLevel logLevel, String message) { 12 | switch (logLevel) { 13 | case DEBUG: 14 | log.debug(message); 15 | break; 16 | case ERROR: 17 | log.error(message); 18 | break; 19 | case FATAL: 20 | log.fatal(message); 21 | break; 22 | case INFO: 23 | log.info(message); 24 | break; 25 | case TRACE: 26 | log.trace(message); 27 | break; 28 | case WARN: 29 | log.warn(message); 30 | break; 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/CommonsQueryCountLoggingHandlerInterceptor.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.CommonsLogLevel; 4 | import org.apache.commons.logging.Log; 5 | import org.apache.commons.logging.LogFactory; 6 | 7 | /** 8 | * Spring {@link org.springframework.web.servlet.HandlerInterceptor} to log the query metrics during a http request 9 | * lifecycle using Apache Commons Logging. 10 | * 11 | * @author Tadaya Tsuyukubo 12 | * @see CommonsQueryCountLoggingServletFilter 13 | * @see CommonsQueryCountLoggingRequestListener 14 | */ 15 | public class CommonsQueryCountLoggingHandlerInterceptor extends AbstractQueryCountLoggingHandlerInterceptor { 16 | 17 | private Log log = LogFactory.getLog(CommonsQueryCountLoggingHandlerInterceptor.class); 18 | private CommonsLogLevel logLevel = CommonsLogLevel.DEBUG; 19 | 20 | public CommonsQueryCountLoggingHandlerInterceptor() { 21 | } 22 | 23 | public CommonsQueryCountLoggingHandlerInterceptor(CommonsLogLevel logLevel) { 24 | this.logLevel = logLevel; 25 | } 26 | 27 | @Override 28 | protected void writeLog(String logEntry) { 29 | CommonsLogUtils.writeLog(log, logLevel, logEntry); 30 | } 31 | 32 | public void setLogLevel(CommonsLogLevel logLevel) { 33 | this.logLevel = logLevel; 34 | } 35 | 36 | @Override 37 | protected void resetLogger(String loggerName) { 38 | this.log = LogFactory.getLog(loggerName); 39 | } 40 | 41 | /** 42 | * Override {@link Log} instance. 43 | * 44 | * @param log new log instance 45 | * @since 1.4.1 46 | */ 47 | public void setLog(Log log) { 48 | this.log = log; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/CommonsQueryCountLoggingRequestListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.CommonsLogLevel; 4 | import org.apache.commons.logging.Log; 5 | import org.apache.commons.logging.LogFactory; 6 | 7 | import javax.servlet.ServletContext; 8 | import javax.servlet.ServletRequestEvent; 9 | 10 | /** 11 | * {@link javax.servlet.ServletRequestListener} to log the query metrics during a http request lifecycle 12 | * using Apache Commons Logging. 13 | * 14 | * @author Tadaya Tsuyukubo 15 | * @see CommonsQueryCountLoggingServletFilter 16 | * @see CommonsQueryCountLoggingHandlerInterceptor 17 | */ 18 | public class CommonsQueryCountLoggingRequestListener extends AbstractQueryCountLoggingRequestListener { 19 | 20 | private static final String LOG_LEVEL_PARAM = "queryCountCommonsLogLevel"; 21 | private static final CommonsLogLevel DEFAULT_LOG_LEVEL = CommonsLogLevel.DEBUG; 22 | 23 | private Log log = LogFactory.getLog(CommonsQueryCountLoggingRequestListener.class); 24 | 25 | @Override 26 | protected void writeLog(ServletRequestEvent servletRequestEvent, String logEntry) { 27 | 28 | ServletContext context = servletRequestEvent.getServletContext(); 29 | String logLevelParam = context.getInitParameter(LOG_LEVEL_PARAM); 30 | CommonsLogLevel logLevel = CommonsLogLevel.nullSafeValueOf(logLevelParam); 31 | if (logLevel == null) { 32 | logLevel = DEFAULT_LOG_LEVEL; 33 | } 34 | 35 | CommonsLogUtils.writeLog(log, logLevel, logEntry); 36 | } 37 | 38 | /** 39 | * Override {@link Log} instance. 40 | * 41 | * @param log new log instance 42 | * @since 1.4.1 43 | */ 44 | public void setLog(Log log) { 45 | this.log = log; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/CommonsQueryCountLoggingServletFilter.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.CommonsLogLevel; 4 | import org.apache.commons.logging.Log; 5 | import org.apache.commons.logging.LogFactory; 6 | 7 | /** 8 | * Servlet Filter to log the query metrics during a http request lifecycle using Apache Commons Logging. 9 | * 10 | *

Some application server reuse threads without cleaning thread local values. Default, this filter clear 11 | * the thread local value used to hold the query metrics. If you do not want to clear the thread local value, 12 | * set "clearQueryCounter", a servlet parameter, to false. 13 | * 14 | *

Also, you can set a log level. 15 | * 16 | * @author Tadaya Tsuyukubo 17 | * @see CommonsQueryCountLoggingHandlerInterceptor 18 | * @see CommonsQueryCountLoggingRequestListener 19 | */ 20 | public class CommonsQueryCountLoggingServletFilter extends AbstractQueryCountLoggingServletFilter { 21 | 22 | private Log log = LogFactory.getLog(CommonsQueryCountLoggingServletFilter.class); 23 | private CommonsLogLevel logLevel = CommonsLogLevel.DEBUG; // default 24 | 25 | public CommonsQueryCountLoggingServletFilter() { 26 | } 27 | 28 | public CommonsQueryCountLoggingServletFilter(CommonsLogLevel logLevel) { 29 | this.logLevel = logLevel; 30 | } 31 | 32 | @Override 33 | protected void initLogLevelFromFilterConfigIfSpecified(String logLevelParam) { 34 | CommonsLogLevel logLevel = CommonsLogLevel.nullSafeValueOf(logLevelParam); 35 | if (logLevel != null) { 36 | this.logLevel = logLevel; 37 | } 38 | } 39 | 40 | @Override 41 | protected void writeLog(String message) { 42 | CommonsLogUtils.writeLog(log, this.logLevel, message); 43 | } 44 | 45 | @Override 46 | protected void resetLogger(String loggerName) { 47 | this.log = LogFactory.getLog(loggerName); 48 | } 49 | 50 | public void setLogLevel(CommonsLogLevel logLevel) { 51 | this.logLevel = logLevel; 52 | } 53 | 54 | /** 55 | * Override {@link Log} instance. 56 | * 57 | * @param log new log instance 58 | * @since 1.4.1 59 | */ 60 | public void setLog(Log log) { 61 | this.log = log; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/DataSourceProxyNativeJdbcExtractor.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.proxy.NativeJdbcExtractUtils; 4 | import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor; 5 | import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractorAdapter; 6 | 7 | import java.sql.CallableStatement; 8 | import java.sql.Connection; 9 | import java.sql.PreparedStatement; 10 | import java.sql.SQLException; 11 | import java.sql.Statement; 12 | 13 | /** 14 | * Spring NativeJdbcExtractor for our proxy classes. 15 | * 16 | *

If delegating extractor is specified, after unwrapping our proxy, delegating 17 | * extractor will be applied. 18 | * 19 | * @author Tadaya Tsuyukubo 20 | */ 21 | public class DataSourceProxyNativeJdbcExtractor extends NativeJdbcExtractorAdapter { 22 | 23 | private NativeJdbcExtractor delegate; 24 | 25 | @Override 26 | protected Connection doGetNativeConnection(Connection con) throws SQLException { 27 | Connection connection = NativeJdbcExtractUtils.getConnection(con); 28 | if (delegate != null) { 29 | connection = delegate.getNativeConnection(connection); 30 | } 31 | return connection; 32 | } 33 | 34 | @Override 35 | public Statement getNativeStatement(Statement stmt) throws SQLException { 36 | Statement statement = NativeJdbcExtractUtils.getStatement(stmt); 37 | if (delegate != null) { 38 | statement = delegate.getNativeStatement(statement); 39 | } 40 | return statement; 41 | } 42 | 43 | @Override 44 | public PreparedStatement getNativePreparedStatement(PreparedStatement ps) throws SQLException { 45 | PreparedStatement preparedStatement = NativeJdbcExtractUtils.getPreparedStatement(ps); 46 | if (delegate != null) { 47 | preparedStatement = delegate.getNativePreparedStatement(preparedStatement); 48 | } 49 | return preparedStatement; 50 | } 51 | 52 | @Override 53 | public CallableStatement getNativeCallableStatement(CallableStatement cs) throws SQLException { 54 | CallableStatement callableStatement = NativeJdbcExtractUtils.getCallableStatement(cs); 55 | if (delegate != null) { 56 | callableStatement = delegate.getNativeCallableStatement(callableStatement); 57 | } 58 | return callableStatement; 59 | } 60 | 61 | public void setDelegate(NativeJdbcExtractor delegate) { 62 | this.delegate = delegate; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/JULQueryCountLoggingHandlerInterceptor.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | /** 7 | * @author Tadaya Tsuyukubo 8 | * @since 1.4.1 9 | */ 10 | public class JULQueryCountLoggingHandlerInterceptor extends AbstractQueryCountLoggingHandlerInterceptor { 11 | 12 | protected Logger logger = Logger.getLogger(JULQueryCountLoggingHandlerInterceptor.class.getName()); 13 | protected Level logLevel = Level.FINE; // default FINE 14 | 15 | public JULQueryCountLoggingHandlerInterceptor() { 16 | } 17 | 18 | public JULQueryCountLoggingHandlerInterceptor(Level logLevel) { 19 | this.logLevel = logLevel; 20 | } 21 | 22 | @Override 23 | protected void writeLog(String message) { 24 | this.logger.log(this.logLevel, message); 25 | } 26 | 27 | public void setLogLevel(Level logLevel) { 28 | this.logLevel = logLevel; 29 | } 30 | 31 | @Override 32 | protected void resetLogger(String loggerName) { 33 | this.logger = Logger.getLogger(loggerName); 34 | } 35 | 36 | public void setLogger(Logger logger) { 37 | this.logger = logger; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/JULQueryCountLoggingRequestListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import javax.servlet.ServletContext; 4 | import javax.servlet.ServletRequestEvent; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | 8 | /** 9 | * @author Tadaya Tsuyukubo 10 | * @since 1.4.1 11 | */ 12 | public class JULQueryCountLoggingRequestListener extends AbstractQueryCountLoggingRequestListener { 13 | 14 | private static final String LOG_LEVEL_PARAM = "queryCountJULLogLevel"; 15 | private static final Level DEFAULT_LOG_LEVEL = Level.FINE; 16 | 17 | protected Logger logger = Logger.getLogger(JULQueryCountLoggingHandlerInterceptor.class.getName()); 18 | 19 | @Override 20 | protected void writeLog(ServletRequestEvent servletRequestEvent, String logEntry) { 21 | ServletContext context = servletRequestEvent.getServletContext(); 22 | String logLevelParam = context.getInitParameter(LOG_LEVEL_PARAM); 23 | Level logLevel = Level.parse(logLevelParam); 24 | if (logLevel == null) { 25 | logLevel = DEFAULT_LOG_LEVEL; 26 | } 27 | 28 | this.logger.log(logLevel, logEntry); 29 | } 30 | 31 | public void setLogger(Logger logger) { 32 | this.logger = logger; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/JULQueryCountLoggingServletFilter.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | /** 7 | * @author Tadaya Tsuyukubo 8 | * @since 1.4.1 9 | */ 10 | public class JULQueryCountLoggingServletFilter extends AbstractQueryCountLoggingServletFilter { 11 | protected Logger logger = Logger.getLogger(JULQueryCountLoggingHandlerInterceptor.class.getName()); 12 | 13 | private Level logLevel = Level.FINE; // default 14 | 15 | public JULQueryCountLoggingServletFilter() { 16 | } 17 | 18 | public JULQueryCountLoggingServletFilter(Level logLevel) { 19 | this.logLevel = logLevel; 20 | } 21 | 22 | @Override 23 | protected void initLogLevelFromFilterConfigIfSpecified(String logLevelParam) { 24 | Level logLevel = Level.parse(logLevelParam); 25 | if (logLevel != null) { 26 | this.logLevel = logLevel; 27 | } 28 | } 29 | 30 | @Override 31 | protected void writeLog(String message) { 32 | this.logger.log(logLevel, message); 33 | } 34 | 35 | @Override 36 | protected void resetLogger(String loggerName) { 37 | this.logger = Logger.getLogger(loggerName); 38 | } 39 | 40 | public void setLogLevel(Level logLevel) { 41 | this.logLevel = logLevel; 42 | } 43 | 44 | public void setLogger(Logger logger) { 45 | this.logger = logger; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/Log4jLogUtils.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.Log4jLogLevel; 4 | import org.apache.logging.log4j.Logger; 5 | 6 | /** 7 | * @author Ivan Jose Sanchez Pagador 8 | * @since 1.8 9 | */ 10 | public class Log4jLogUtils { 11 | 12 | public static void writeLog(Logger logger, Log4jLogLevel logLevel, String message) { 13 | switch (logLevel) { 14 | case DEBUG: 15 | logger.debug(message); 16 | break; 17 | case ERROR: 18 | logger.error(message); 19 | break; 20 | case INFO: 21 | logger.info(message); 22 | break; 23 | case TRACE: 24 | logger.trace(message); 25 | break; 26 | case WARN: 27 | logger.warn(message); 28 | break; 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/Log4jQueryCountLoggingHandlerInterceptor.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.Log4jLogLevel; 4 | import net.ttddyy.dsproxy.listener.logging.Log4jQueryLoggingListener; 5 | import org.apache.logging.log4j.LogManager; 6 | import org.apache.logging.log4j.Logger; 7 | 8 | /** 9 | * @author Ivan Jose Sanchez Pagador 10 | * @since 1.8 11 | */ 12 | public class Log4jQueryCountLoggingHandlerInterceptor extends AbstractQueryCountLoggingHandlerInterceptor { 13 | 14 | protected org.apache.logging.log4j.Logger log = LogManager.getLogger(Log4jQueryCountLoggingHandlerInterceptor.class.getName()); 15 | protected Log4jLogLevel logLevel = Log4jLogLevel.DEBUG; // default DEBUG 16 | 17 | public Log4jQueryCountLoggingHandlerInterceptor() { 18 | } 19 | 20 | public Log4jQueryCountLoggingHandlerInterceptor(Log4jLogLevel logLevel) { 21 | this.logLevel = logLevel; 22 | } 23 | 24 | @Override 25 | protected void writeLog(String logEntry) { 26 | Log4jLogUtils.writeLog(log, logLevel, logEntry); 27 | } 28 | 29 | public void setLogLevel(Log4jLogLevel logLevel) { 30 | this.logLevel = logLevel; 31 | } 32 | 33 | @Override 34 | protected void resetLogger(String loggerName) { 35 | this.log = LogManager.getLogger(loggerName); 36 | } 37 | 38 | /** 39 | * Override {@link Logger} instance. 40 | * 41 | * @param logger new log instance 42 | */ 43 | public void setLogger(Logger logger) { 44 | this.log = logger; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/Log4jQueryCountLoggingRequestListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.Log4jLogLevel; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | import javax.servlet.ServletContext; 8 | import javax.servlet.ServletRequestEvent; 9 | 10 | /** 11 | * @author Ivan Jose Sanchez Pagador 12 | * @since 1.8 13 | */ 14 | public class Log4jQueryCountLoggingRequestListener extends AbstractQueryCountLoggingRequestListener { 15 | 16 | private static final String LOG_LEVEL_PARAM = "queryCountLog4jLogLevel"; 17 | private static final Log4jLogLevel DEFAULT_LOG_LEVEL = Log4jLogLevel.DEBUG; 18 | private Logger log = LogManager.getLogger(Log4jQueryCountLoggingRequestListener.class.getName()); 19 | 20 | 21 | @Override 22 | protected void writeLog(ServletRequestEvent servletRequestEvent, String logEntry) { 23 | ServletContext context = servletRequestEvent.getServletContext(); 24 | String logLevelParam = context.getInitParameter(LOG_LEVEL_PARAM); 25 | Log4jLogLevel logLevel = Log4jLogLevel.nullSafeValueOf(logLevelParam); 26 | if (logLevel == null) { 27 | logLevel = DEFAULT_LOG_LEVEL; 28 | } 29 | 30 | Log4jLogUtils.writeLog(log, logLevel, logEntry); 31 | } 32 | 33 | /** 34 | * Override {@link Logger} instance. 35 | * 36 | * @param logger new log instance 37 | */ 38 | public void setLogger(Logger logger) { 39 | this.log = logger; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/Log4jQueryCountLoggingServletFilter.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.Log4jLogLevel; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | /** 8 | * @author Ivan Jose Sanchez Pagador 9 | * @since 1.8 10 | */ 11 | public class Log4jQueryCountLoggingServletFilter extends AbstractQueryCountLoggingServletFilter { 12 | 13 | protected Logger log = LogManager.getLogger(Log4jQueryCountLoggingServletFilter.class.getName()); 14 | private Log4jLogLevel logLevel = Log4jLogLevel.DEBUG; 15 | 16 | @Override 17 | protected void initLogLevelFromFilterConfigIfSpecified(String logLevelParam) { 18 | Log4jLogLevel logLevel = Log4jLogLevel.nullSafeValueOf(logLevelParam); 19 | if (logLevel != null) { 20 | this.logLevel = logLevel; 21 | } 22 | } 23 | 24 | @Override 25 | protected void writeLog(String message) { 26 | Log4jLogUtils.writeLog(log, this.logLevel, message); 27 | } 28 | 29 | @Override 30 | protected void resetLogger(String loggerName) { 31 | this.log = LogManager.getLogger(loggerName); 32 | } 33 | 34 | public void setLogLevel(Log4jLogLevel logLevel) { 35 | this.logLevel = logLevel; 36 | } 37 | 38 | /** 39 | * Override {@link Logger} instance. 40 | * 41 | * @param log new log instance 42 | */ 43 | public void setLogger(Logger log) { 44 | this.log = log; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/ProxyConnectionAdvice.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.ConnectionIdManager; 4 | import net.ttddyy.dsproxy.ConnectionInfo; 5 | import net.ttddyy.dsproxy.proxy.JdbcProxyFactory; 6 | import net.ttddyy.dsproxy.proxy.ProxyConfig; 7 | import org.aopalliance.intercept.MethodInterceptor; 8 | import org.aopalliance.intercept.MethodInvocation; 9 | 10 | import java.sql.Connection; 11 | 12 | /** 13 | * Support injecting proxies by AOP. 14 | * 15 | * @author Tadaya Tsuyukubo 16 | */ 17 | public class ProxyConnectionAdvice implements MethodInterceptor { 18 | 19 | private ProxyConfig proxyConfig = ProxyConfig.Builder.create().build(); // default 20 | 21 | public Object invoke(MethodInvocation invocation) throws Throwable { 22 | 23 | Object retVal = invocation.proceed(); 24 | 25 | // only when return value is connection, return proxy. 26 | if (!(retVal instanceof Connection)) { 27 | return retVal; 28 | } 29 | 30 | Connection conn = (Connection) retVal; 31 | String connId = this.proxyConfig.getConnectionIdManager().getId(conn); 32 | ConnectionInfo connectionInfo = new ConnectionInfo(); 33 | connectionInfo.setConnectionId(connId); 34 | connectionInfo.setDataSourceName(""); 35 | if (this.proxyConfig.isRetrieveIsolationLevel()) { 36 | connectionInfo.setIsolationLevel(conn.getTransactionIsolation()); 37 | } 38 | 39 | return this.proxyConfig.getJdbcProxyFactory().createConnection((Connection) retVal, connectionInfo, this.proxyConfig); 40 | } 41 | 42 | 43 | public JdbcProxyFactory getJdbcProxyFactory() { 44 | return this.proxyConfig.getJdbcProxyFactory(); 45 | } 46 | 47 | public void setJdbcProxyFactory(JdbcProxyFactory jdbcProxyFactory) { 48 | this.proxyConfig = ProxyConfig.Builder.from(this.proxyConfig) 49 | .jdbcProxyFactory(jdbcProxyFactory) 50 | .build(); 51 | } 52 | 53 | /** 54 | * @since 1.4.2 55 | */ 56 | public ConnectionIdManager getConnectionIdManager() { 57 | return this.proxyConfig.getConnectionIdManager(); 58 | } 59 | 60 | /** 61 | * @since 1.4.2 62 | */ 63 | public void setConnectionIdManager(ConnectionIdManager connectionIdManager) { 64 | this.proxyConfig = ProxyConfig.Builder.from(this.proxyConfig) 65 | .connectionIdManager(connectionIdManager) 66 | .build(); 67 | } 68 | 69 | /** 70 | * @since 1.4.3 71 | */ 72 | public ProxyConfig getProxyConfig() { 73 | return this.proxyConfig; 74 | } 75 | 76 | /** 77 | * @since 1.4.3 78 | */ 79 | public void setProxyConfig(ProxyConfig proxyConfig) { 80 | this.proxyConfig = proxyConfig; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/QueryCountLogEntryCreator.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.QueryCount; 4 | 5 | /** 6 | * @author Tadaya Tsuyukubo 7 | */ 8 | public interface QueryCountLogEntryCreator { 9 | 10 | String getLogMessage(String datasourceName, QueryCount queryCount); 11 | String getLogMessageAsJson(String datasourceName, QueryCount queryCount); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/QueryCounterClearFilter.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.QueryCountHolder; 4 | 5 | import javax.servlet.Filter; 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.FilterConfig; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | import java.io.IOException; 12 | 13 | /** 14 | * Servlet filter to clear the {@link net.ttddyy.dsproxy.QueryCount} stored in thread local at the end of the 15 | * http servlet request lifecycle when {@link net.ttddyy.dsproxy.listener.DataSourceQueryCountListener} is used. 16 | * 17 | * @author Tadaya Tsuyukubo 18 | * @see QueryCounterClearHandlerInterceptor 19 | * @see QueryCounterClearServletRequestListener 20 | */ 21 | public class QueryCounterClearFilter implements Filter { 22 | public void init(FilterConfig filterConfig) throws ServletException { 23 | } 24 | 25 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 26 | chain.doFilter(request, response); 27 | 28 | QueryCountHolder.clear(); 29 | } 30 | 31 | public void destroy() { 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/QueryCounterClearHandlerInterceptor.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.QueryCountHolder; 4 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | /** 10 | * Spring {@link org.springframework.web.servlet.HandlerInterceptor} to clear {@link net.ttddyy.dsproxy.QueryCount} 11 | * stored in thread local when {@link net.ttddyy.dsproxy.listener.DataSourceQueryCountListener} is used. 12 | * 13 | * @author Tadaya Tsuyukubo 14 | * @see QueryCounterClearFilter 15 | * @see QueryCounterClearServletRequestListener 16 | */ 17 | public class QueryCounterClearHandlerInterceptor extends HandlerInterceptorAdapter { 18 | 19 | @Override 20 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 21 | QueryCountHolder.clear(); 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/QueryCounterClearServletRequestListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.QueryCountHolder; 4 | 5 | import javax.servlet.ServletRequestEvent; 6 | import javax.servlet.ServletRequestListener; 7 | 8 | /** 9 | * {@link javax.servlet.ServletRequestListener} to clear {@link net.ttddyy.dsproxy.QueryCount} stored in 10 | * thread local when {@link net.ttddyy.dsproxy.listener.DataSourceQueryCountListener} is used. 11 | * 12 | * @author Tadaya Tsuyukubo 13 | * @see QueryCounterClearFilter 14 | * @see QueryCounterClearHandlerInterceptor 15 | */ 16 | public class QueryCounterClearServletRequestListener implements ServletRequestListener { 17 | 18 | public void requestInitialized(ServletRequestEvent sre) { 19 | } 20 | 21 | public void requestDestroyed(ServletRequestEvent sre) { 22 | QueryCountHolder.clear(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/SLF4JLogUtils.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; 4 | import org.slf4j.Logger; 5 | 6 | /** 7 | * @author Tadaya Tsuyukubo 8 | */ 9 | public class SLF4JLogUtils { 10 | 11 | public static void writeLog(Logger logger, SLF4JLogLevel logLevel, String message) { 12 | switch (logLevel) { 13 | case DEBUG: 14 | logger.debug(message); 15 | break; 16 | case ERROR: 17 | logger.error(message); 18 | break; 19 | case INFO: 20 | logger.info(message); 21 | break; 22 | case TRACE: 23 | logger.trace(message); 24 | break; 25 | case WARN: 26 | logger.warn(message); 27 | break; 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/SLF4JQueryCountLoggingHandlerInterceptor.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * @author Tadaya Tsuyukubo 9 | */ 10 | public class SLF4JQueryCountLoggingHandlerInterceptor extends AbstractQueryCountLoggingHandlerInterceptor { 11 | 12 | private Logger logger = LoggerFactory.getLogger(SLF4JQueryCountLoggingHandlerInterceptor.class); 13 | private SLF4JLogLevel logLevel = SLF4JLogLevel.DEBUG; 14 | 15 | public SLF4JQueryCountLoggingHandlerInterceptor() { 16 | } 17 | 18 | public SLF4JQueryCountLoggingHandlerInterceptor(SLF4JLogLevel logLevel) { 19 | this.logLevel = logLevel; 20 | } 21 | 22 | @Override 23 | protected void writeLog(String logEntry) { 24 | SLF4JLogUtils.writeLog(logger, logLevel, logEntry); 25 | } 26 | 27 | public void setLogLevel(SLF4JLogLevel logLevel) { 28 | this.logLevel = logLevel; 29 | } 30 | 31 | @Override 32 | protected void resetLogger(String loggerName) { 33 | this.logger = LoggerFactory.getLogger(loggerName); 34 | } 35 | 36 | /** 37 | * Override {@link Logger} instance. 38 | * 39 | * @param logger new log instance 40 | * @since 1.4.1 41 | */ 42 | public void setLogger(Logger logger) { 43 | this.logger = logger; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/SLF4JQueryCountLoggingRequestListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import javax.servlet.ServletContext; 8 | import javax.servlet.ServletRequestEvent; 9 | 10 | /** 11 | * @author Tadaya Tsuyukubo 12 | */ 13 | public class SLF4JQueryCountLoggingRequestListener extends AbstractQueryCountLoggingRequestListener { 14 | 15 | private static final String LOG_LEVEL_PARAM = "queryCountSLF4JLogLevel"; 16 | private static final SLF4JLogLevel DEFAULT_LOG_LEVEL = SLF4JLogLevel.DEBUG; 17 | 18 | private Logger logger = LoggerFactory.getLogger(SLF4JQueryCountLoggingRequestListener.class); 19 | 20 | @Override 21 | protected void writeLog(ServletRequestEvent servletRequestEvent, String logEntry) { 22 | ServletContext context = servletRequestEvent.getServletContext(); 23 | String logLevelParam = context.getInitParameter(LOG_LEVEL_PARAM); 24 | SLF4JLogLevel logLevel = SLF4JLogLevel.nullSafeValueOf(logLevelParam); 25 | if (logLevel == null) { 26 | logLevel = DEFAULT_LOG_LEVEL; 27 | } 28 | 29 | SLF4JLogUtils.writeLog(logger, logLevel, logEntry); 30 | } 31 | 32 | /** 33 | * Override {@link Logger} instance. 34 | * 35 | * @param logger new log instance 36 | * @since 1.4.1 37 | */ 38 | public void setLogger(Logger logger) { 39 | this.logger = logger; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/SLF4JQueryCountLoggingServletFilter.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * @author Tadaya Tsuyukubo 9 | */ 10 | public class SLF4JQueryCountLoggingServletFilter extends AbstractQueryCountLoggingServletFilter { 11 | 12 | private Logger logger = LoggerFactory.getLogger(SLF4JQueryCountLoggingServletFilter.class); 13 | private SLF4JLogLevel logLevel = SLF4JLogLevel.DEBUG; 14 | 15 | @Override 16 | protected void initLogLevelFromFilterConfigIfSpecified(String logLevelParam) { 17 | SLF4JLogLevel logLevel = SLF4JLogLevel.nullSafeValueOf(logLevelParam); 18 | if (logLevel != null) { 19 | this.logLevel = logLevel; 20 | } 21 | } 22 | 23 | @Override 24 | protected void writeLog(String message) { 25 | SLF4JLogUtils.writeLog(logger, this.logLevel, message); 26 | } 27 | 28 | @Override 29 | protected void resetLogger(String loggerName) { 30 | this.logger = LoggerFactory.getLogger(loggerName); 31 | } 32 | 33 | public void setLogLevel(SLF4JLogLevel logLevel) { 34 | this.logLevel = logLevel; 35 | } 36 | 37 | /** 38 | * Override {@link Logger} instance. 39 | * 40 | * @param logger new log instance 41 | * @since 1.4.1 42 | */ 43 | public void setLogger(Logger logger) { 44 | this.logger = logger; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/SystemOutQueryCountLoggingHandlerInterceptor.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | /** 4 | * {@link org.springframework.web.servlet.HandlerInterceptor} to log query metrics to {@link java.lang.System#out}. 5 | * 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.3 8 | */ 9 | public class SystemOutQueryCountLoggingHandlerInterceptor extends AbstractQueryCountLoggingHandlerInterceptor { 10 | @Override 11 | protected void writeLog(String logEntry) { 12 | System.out.println(logEntry); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/SystemOutQueryCountLoggingRequestListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import javax.servlet.ServletRequestEvent; 4 | 5 | /** 6 | * {@link javax.servlet.ServletRequestListener} to log query metrics per http request using {@link java.lang.System#out}. 7 | * 8 | * @author Tadaya Tsuyukubo 9 | * @since 1.4.1 10 | */ 11 | public class SystemOutQueryCountLoggingRequestListener extends AbstractQueryCountLoggingRequestListener { 12 | 13 | @Override 14 | protected void writeLog(ServletRequestEvent servletRequestEvent, String logEntry) { 15 | System.out.println(logEntry); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/SystemOutQueryCountLoggingServletFilter.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | /** 4 | * Servlet Filter to log query metrics per http request using {@link java.lang.System#out}. 5 | * 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.3 8 | */ 9 | public class SystemOutQueryCountLoggingServletFilter extends AbstractQueryCountLoggingServletFilter { 10 | @Override 11 | protected void initLogLevelFromFilterConfigIfSpecified(String logLevelParam) { 12 | // NO-OP 13 | } 14 | 15 | @Override 16 | protected void writeLog(String message) { 17 | System.out.println(message); 18 | } 19 | 20 | @Override 21 | protected void resetLogger(String loggerName) { 22 | // NO-OP 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/support/tags/MetricsTag.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support.tags; 2 | 3 | import net.ttddyy.dsproxy.QueryCount; 4 | import net.ttddyy.dsproxy.QueryCountHolder; 5 | 6 | import javax.servlet.jsp.JspException; 7 | import javax.servlet.jsp.JspWriter; 8 | import javax.servlet.jsp.tagext.SimpleTagSupport; 9 | import java.io.IOException; 10 | 11 | /** 12 | * Implementation of "metrics" custom tag. 13 | * 14 | * @author Tadaya Tsuyukubo 15 | */ 16 | public class MetricsTag extends SimpleTagSupport { 17 | private String dataSource; 18 | private String metric; 19 | 20 | @Override 21 | public void doTag() throws JspException, IOException { 22 | 23 | if (metric == null || "".equals(metric)) { 24 | return; 25 | } 26 | 27 | final QueryCount count; 28 | if (dataSource == null || "".equals(dataSource)) { 29 | count = QueryCountHolder.getGrandTotal(); 30 | } else { 31 | count = QueryCountHolder.get(dataSource); 32 | } 33 | 34 | if (count == null) { 35 | return; 36 | } 37 | 38 | final StringBuilder sb = new StringBuilder(); 39 | if ("select".equalsIgnoreCase(metric)) { 40 | sb.append(count.getSelect()); 41 | } else if ("insert".equalsIgnoreCase(metric)) { 42 | sb.append(count.getInsert()); 43 | } else if ("update".equalsIgnoreCase(metric)) { 44 | sb.append(count.getUpdate()); 45 | } else if ("delete".equalsIgnoreCase(metric)) { 46 | sb.append(count.getDelete()); 47 | } else if ("other".equalsIgnoreCase(metric)) { 48 | sb.append(count.getOther()); 49 | } else if ("statement".equalsIgnoreCase(metric)) { 50 | sb.append(count.getStatement()); 51 | } else if ("prepared".equalsIgnoreCase(metric)) { 52 | sb.append(count.getPrepared()); 53 | } else if ("callable".equalsIgnoreCase(metric)) { 54 | sb.append(count.getCallable()); 55 | } else if ("total".equalsIgnoreCase(metric)) { 56 | sb.append(count.getTotal()); 57 | } else if ("success".equalsIgnoreCase(metric)) { 58 | sb.append(count.getSuccess()); 59 | } else if ("failure".equalsIgnoreCase(metric)) { 60 | sb.append(count.getFailure()); 61 | } else if ("time".equalsIgnoreCase(metric)) { 62 | sb.append(count.getTime()); 63 | } 64 | 65 | final JspWriter writer = getJspContext().getOut(); 66 | writer.print(sb.toString()); 67 | } 68 | 69 | public String getDataSource() { 70 | return dataSource; 71 | } 72 | 73 | public void setDataSource(String dataSource) { 74 | this.dataSource = dataSource; 75 | } 76 | 77 | public String getMetric() { 78 | return metric; 79 | } 80 | 81 | public void setMetric(String metric) { 82 | this.metric = metric; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/transform/NoOpParameterTransformer.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.transform; 2 | 3 | /** 4 | * No operation implementation of {@link ParameterTransformer}. 5 | * 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.2 8 | */ 9 | public class NoOpParameterTransformer implements ParameterTransformer { 10 | 11 | public void transformParameters(ParameterReplacer replacer, TransformInfo transformInfo) { 12 | // do nothing 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/transform/NoOpQueryTransformer.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.transform; 2 | 3 | /** 4 | * No operation implementation of {@link QueryTransformer}. 5 | * 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.2 8 | */ 9 | public class NoOpQueryTransformer implements QueryTransformer { 10 | public String transformQuery(TransformInfo transformInfo) { 11 | return transformInfo.getQuery(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/transform/ParameterTransformer.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.transform; 2 | 3 | /** 4 | * @author Tadaya Tsuyukubo 5 | * @since 1.2 6 | */ 7 | public interface ParameterTransformer { 8 | 9 | static ParameterTransformer DEFAULT = new NoOpParameterTransformer(); 10 | 11 | void transformParameters(ParameterReplacer replacer, TransformInfo transformInfo); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/transform/QueryTransformer.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.transform; 2 | 3 | /** 4 | * Interceptor that can transform the query statement. 5 | * 6 | * @author Tadaya Tsuyukubo 7 | * @since 1.2 8 | */ 9 | public interface QueryTransformer { 10 | 11 | static QueryTransformer DEFAULT = new NoOpQueryTransformer(); 12 | 13 | String transformQuery(TransformInfo transformInfo); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/ttddyy/dsproxy/transform/TransformInfo.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.transform; 2 | 3 | import java.sql.Statement; 4 | 5 | /** 6 | * Hold context information for {@link ParameterTransformer#transformParameters(ParameterReplacer, TransformInfo)}. 7 | * 8 | *

    9 | *
  • clazz: calling class. {@link java.sql.PreparedStatement} or {@link java.sql.CallableStatement} 10 | *
  • dataSourceName: datasource name 11 | *
  • query: query string 12 | *
  • isBatch: true when called in batch 13 | *
  • count: current number of call in batch. 0 origin. 0 if call is not batched 14 | *
15 | * 16 | * Semantics of {@link #isBatch()}: 17 | *

For {@link QueryTransformer}, {@link #isBatch()} is only true when {@link Statement#addBatch(String)} is called. 18 | * {@link #isBatch()} is always false for {@link java.sql.PreparedStatement} and {@link java.sql.CallableStatement}. 19 | * For {@link ParameterTransformer}, {@link #isBatch()} returns true when 20 | * {@link java.sql.PreparedStatement#addBatch()} or {@link java.sql.CallableStatement#addBatch()} is called. 21 | * 22 | * @author Tadaya Tsuyukubo 23 | * @see net.ttddyy.dsproxy.transform.ParameterTransformer 24 | * @see net.ttddyy.dsproxy.transform.QueryTransformer 25 | * @since 1.2 26 | */ 27 | public class TransformInfo { 28 | 29 | private Class clazz; 30 | private String dataSourceName; 31 | private String query; 32 | private boolean isBatch; 33 | private int count; 34 | 35 | public TransformInfo() { 36 | } 37 | 38 | public TransformInfo(Class clazz, String dataSourceName, String query, boolean batch, int count) { 39 | this.clazz = clazz; 40 | this.dataSourceName = dataSourceName; 41 | this.query = query; 42 | isBatch = batch; 43 | this.count = count; 44 | } 45 | 46 | public Class getClazz() { 47 | return clazz; 48 | } 49 | 50 | public void setClazz(Class clazz) { 51 | this.clazz = clazz; 52 | } 53 | 54 | public String getDataSourceName() { 55 | return dataSourceName; 56 | } 57 | 58 | public void setDataSourceName(String dataSourceName) { 59 | this.dataSourceName = dataSourceName; 60 | } 61 | 62 | public String getQuery() { 63 | return query; 64 | } 65 | 66 | public void setQuery(String query) { 67 | this.query = query; 68 | } 69 | 70 | public boolean isBatch() { 71 | return isBatch; 72 | } 73 | 74 | public void setBatch(boolean batch) { 75 | isBatch = batch; 76 | } 77 | 78 | /** 79 | * Current order in batch. 80 | * 0 origin. always 0 if called in non-batch. 81 | * 82 | * @return current order in batch 83 | */ 84 | public int getCount() { 85 | return count; 86 | } 87 | 88 | public void setCount(int count) { 89 | this.count = count; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/dsproxy.tld: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 1.0 7 | 1.2 8 | dsproxy 9 | http://www.ttddyy.net/dsproxy/tags 10 | 11 | datasource-proxy tag library 12 | 13 | 14 | 15 | metrics 16 | net.ttddyy.dsproxy.support.tags.MetricsTag 17 | empty 18 | 19 | Retrieve QueryCount values from QueryCountHolder. You need to register DataSourceQueryCountListener to use this tag. 20 | 21 | 22 | 23 | dataSource 24 | false 25 | 26 | Specify a datasource name to display the metric. 27 | If not specified, metric value will be summed up from all datasource. 28 | 29 | 30 | 31 | 32 | metric 33 | true 34 | 35 | metric to display. 36 | Needs to be one of the followings: 37 | "select": num of select queries 38 | "insert": num of insert queries 39 | "update": num of update queries 40 | "delete": num of delete queries 41 | "other" : num of other queries 42 | "statement" : total num of statements 43 | "prepared" : total num of prepared statements 44 | "callable" : total num of callable statements 45 | "total" : total num of queries 46 | "success" : num of success queries 47 | "failure" : num of failure queries 48 | "time": query execution time 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/ExecutionInfoTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | import org.junit.Test; 4 | 5 | import java.sql.CallableStatement; 6 | import java.sql.PreparedStatement; 7 | import java.sql.Statement; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | import static org.mockito.Mockito.mock; 11 | 12 | /** 13 | * @author Tadaya Tsuyukubo 14 | * @since 1.3.1 15 | */ 16 | public class ExecutionInfoTest { 17 | 18 | @Test 19 | public void statementType() { 20 | Statement statement = mock(Statement.class); 21 | PreparedStatement prepared = mock(PreparedStatement.class); 22 | CallableStatement callable = mock(CallableStatement.class); 23 | 24 | ConnectionInfo connectionInfo = new ConnectionInfo(); 25 | connectionInfo.setDataSourceName(""); 26 | 27 | ExecutionInfo executionInfo; 28 | executionInfo = new ExecutionInfo(connectionInfo, statement, true, 0, null, null); 29 | assertThat(executionInfo.getStatementType()).isEqualTo(StatementType.STATEMENT); 30 | 31 | executionInfo = new ExecutionInfo(connectionInfo, prepared, true, 0, null, null); 32 | assertThat(executionInfo.getStatementType()).isEqualTo(StatementType.PREPARED); 33 | 34 | executionInfo = new ExecutionInfo(connectionInfo, callable, true, 0, null, null); 35 | assertThat(executionInfo.getStatementType()).isEqualTo(StatementType.CALLABLE); 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/LastQueryListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | import net.ttddyy.dsproxy.listener.QueryExecutionListener; 4 | import net.ttddyy.dsproxy.ExecutionInfo; 5 | import net.ttddyy.dsproxy.QueryInfo; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * @author Tadaya Tsuyukubo 12 | */ 13 | public class LastQueryListener implements QueryExecutionListener { 14 | private ExecutionInfo beforeExecInfo; 15 | private ExecutionInfo afterExecInfo; 16 | private List beforeQueries = new ArrayList(); 17 | private List afterQueries = new ArrayList(); 18 | 19 | public void beforeQuery(ExecutionInfo execInfo, List queryInfoList) { 20 | beforeExecInfo = execInfo; 21 | beforeQueries = queryInfoList; 22 | } 23 | 24 | public void afterQuery(ExecutionInfo execInfo, List queryInfoList) { 25 | afterExecInfo = execInfo; 26 | afterQueries = queryInfoList; 27 | } 28 | 29 | public ExecutionInfo getBeforeExecInfo() { 30 | return beforeExecInfo; 31 | } 32 | 33 | public ExecutionInfo getAfterExecInfo() { 34 | return afterExecInfo; 35 | } 36 | 37 | public List getBeforeQueries() { 38 | return beforeQueries; 39 | } 40 | 41 | public List getAfterQueries() { 42 | return afterQueries; 43 | } 44 | } -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/StatementTypeTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | import org.junit.Test; 4 | 5 | import java.sql.CallableStatement; 6 | import java.sql.PreparedStatement; 7 | import java.sql.Statement; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | import static org.mockito.Mockito.mock; 11 | 12 | /** 13 | * @author Tadaya Tsuyukubo 14 | * @since 1.3.1 15 | */ 16 | public class StatementTypeTest { 17 | 18 | @Test 19 | public void valueOf() { 20 | Statement statement = mock(Statement.class); 21 | PreparedStatement prepared = mock(PreparedStatement.class); 22 | CallableStatement callable = mock(CallableStatement.class); 23 | 24 | assertThat(StatementType.valueOf(statement)).isEqualTo(StatementType.STATEMENT); 25 | assertThat(StatementType.valueOf(prepared)).isEqualTo(StatementType.PREPARED); 26 | assertThat(StatementType.valueOf(callable)).isEqualTo(StatementType.CALLABLE); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/TestListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | import net.ttddyy.dsproxy.listener.QueryExecutionListener; 4 | import net.ttddyy.dsproxy.ExecutionInfo; 5 | import net.ttddyy.dsproxy.QueryInfo; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author Tadaya Tsuyukubo 11 | */ 12 | public class TestListener implements QueryExecutionListener { 13 | int beforeCount = 0; 14 | int afterCount = 0; 15 | 16 | public void beforeQuery(ExecutionInfo execInfo, List queryInfoList) { 17 | beforeCount++; 18 | } 19 | 20 | public void afterQuery(ExecutionInfo execInfo, List queryInfoList) { 21 | afterCount++; 22 | } 23 | 24 | 25 | public int getBeforeCount() { 26 | return beforeCount; 27 | } 28 | 29 | public int getAfterCount() { 30 | return afterCount; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/TestUtils.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy; 2 | 3 | import org.hsqldb.jdbc.JDBCDataSource; 4 | 5 | import javax.sql.DataSource; 6 | import java.sql.Connection; 7 | import java.sql.ResultSet; 8 | import java.sql.Statement; 9 | 10 | /** 11 | * @author Tadaya Tsuyukubo 12 | */ 13 | public class TestUtils { 14 | 15 | public static DataSource getDataSourceWithData() throws Exception { 16 | JDBCDataSource dataSource = new JDBCDataSource(); 17 | dataSource.setDatabase("jdbc:hsqldb:mem:aname"); 18 | dataSource.setUser("sa"); 19 | 20 | executeQuery(dataSource, 21 | "create table emp ( id integer primary key, name varchar(10) );", 22 | "insert into emp ( id, name ) values (1, 'foo');", 23 | "insert into emp ( id, name ) values (2, 'bar');" 24 | ); 25 | 26 | executeQuery(dataSource, 27 | "create table emp_with_auto_id ( id integer generated by default as identity primary key, name varchar(10) );", 28 | "insert into emp_with_auto_id ( name ) values ('foo');", 29 | "insert into emp_with_auto_id ( name ) values ('bar');" 30 | ); 31 | 32 | return dataSource; 33 | } 34 | 35 | private static void executeQuery(DataSource dataSource, String... queries) throws Exception { 36 | Connection conn = dataSource.getConnection(); 37 | Statement stmt = conn.createStatement(); 38 | for (String query : queries) { 39 | stmt.execute(query); 40 | } 41 | conn.close(); 42 | } 43 | 44 | public static void shutdown(DataSource dataSource) throws Exception { 45 | executeQuery(dataSource, "shutdown;"); 46 | } 47 | 48 | public static int countTable(DataSource dataSource, String tableName) throws Exception { 49 | Connection conn = dataSource.getConnection(); 50 | Statement stmt = conn.createStatement(); 51 | ResultSet rs = stmt.executeQuery("select count(*) from " + tableName); 52 | rs.next(); 53 | conn.close(); 54 | return rs.getInt(1); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/listener/CallCheckMethodExecutionListener.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | /** 4 | * @author Tadaya Tsuyukubo 5 | */ 6 | public class CallCheckMethodExecutionListener implements MethodExecutionListener { 7 | 8 | boolean isBeforeMethodCalled; 9 | boolean isAfterMethodCalled; 10 | MethodExecutionContext beforeMethodContext; 11 | MethodExecutionContext afterMethodContext; 12 | 13 | @Override 14 | public void beforeMethod(MethodExecutionContext executionContext) { 15 | this.isBeforeMethodCalled = true; 16 | this.beforeMethodContext = executionContext; 17 | } 18 | 19 | @Override 20 | public void afterMethod(MethodExecutionContext executionContext) { 21 | this.isAfterMethodCalled = true; 22 | this.afterMethodContext = executionContext; 23 | } 24 | 25 | public boolean isBeforeMethodCalled() { 26 | return isBeforeMethodCalled; 27 | } 28 | 29 | public boolean isAfterMethodCalled() { 30 | return isAfterMethodCalled; 31 | } 32 | 33 | public MethodExecutionContext getBeforeMethodContext() { 34 | return beforeMethodContext; 35 | } 36 | 37 | public MethodExecutionContext getAfterMethodContext() { 38 | return afterMethodContext; 39 | } 40 | 41 | public void reset() { 42 | this.isBeforeMethodCalled = false; 43 | this.isAfterMethodCalled = false; 44 | this.beforeMethodContext = null; 45 | this.afterMethodContext = null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/listener/MethodExecutionListenerTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import net.ttddyy.dsproxy.TestUtils; 4 | import net.ttddyy.dsproxy.support.ProxyDataSource; 5 | import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import javax.sql.DataSource; 11 | import java.sql.Connection; 12 | import java.sql.ResultSet; 13 | import java.sql.Statement; 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | 17 | /** 18 | * @author Tadaya Tsuyukubo 19 | */ 20 | public class MethodExecutionListenerTest { 21 | 22 | private DataSource jdbcDataSource; 23 | 24 | @Before 25 | public void setup() throws Exception { 26 | // real datasource 27 | jdbcDataSource = TestUtils.getDataSourceWithData(); 28 | } 29 | 30 | @After 31 | public void teardown() throws Exception { 32 | TestUtils.shutdown(jdbcDataSource); 33 | } 34 | 35 | 36 | @Test 37 | public void replaceMethodArgument() throws Throwable { 38 | MethodExecutionListener methodListener = new MethodExecutionListener() { 39 | @Override 40 | public void beforeMethod(MethodExecutionContext executionContext) { 41 | 42 | // replace query to find id=2 43 | if ("executeQuery".equals(executionContext.getMethod().getName())) { 44 | executionContext.setMethodArgs(new Object[]{"select * from emp where id=2"}); 45 | } 46 | } 47 | 48 | @Override 49 | public void afterMethod(MethodExecutionContext executionContext) { 50 | 51 | } 52 | }; 53 | 54 | ProxyDataSource ds = ProxyDataSourceBuilder.create(this.jdbcDataSource).methodListener(methodListener).build(); 55 | Connection conn = ds.getConnection(); 56 | Statement statement = conn.createStatement(); 57 | ResultSet rs = statement.executeQuery("select * from emp where id=1"); 58 | rs.next(); 59 | assertThat(rs.getInt("id")).isEqualTo(2); 60 | assertThat(rs.getString("name")).isEqualTo("bar"); 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/listener/QueryUtilsTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import net.ttddyy.dsproxy.QueryType; 4 | import org.junit.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | /** 9 | * @author Tadaya Tsuyukubo 10 | * @since 1.4 11 | */ 12 | public class QueryUtilsTest { 13 | 14 | @Test 15 | public void removeCommentAndWhiteSpace(){ 16 | assertThat(QueryUtils.removeCommentAndWhiteSpace("")).isEqualTo(""); 17 | assertThat(QueryUtils.removeCommentAndWhiteSpace(null)).isEqualTo(null); 18 | assertThat(QueryUtils.removeCommentAndWhiteSpace("--\n")).isEqualTo(""); 19 | assertThat(QueryUtils.removeCommentAndWhiteSpace("\n\n\n")).isEqualTo(""); 20 | assertThat(QueryUtils.removeCommentAndWhiteSpace("/* abc */")).isEqualTo(""); 21 | assertThat(QueryUtils.removeCommentAndWhiteSpace(" ")).isEqualTo(""); 22 | assertThat(QueryUtils.removeCommentAndWhiteSpace(" aa ")).isEqualTo("aa"); 23 | assertThat(QueryUtils.removeCommentAndWhiteSpace(" aa")).isEqualTo("aa"); 24 | assertThat(QueryUtils.removeCommentAndWhiteSpace("aa ")).isEqualTo("aa"); 25 | } 26 | 27 | @Test 28 | public void getQueryType(){ 29 | assertThat(QueryUtils.getQueryType("")).isEqualTo(QueryType.OTHER); 30 | assertThat(QueryUtils.getQueryType(null)).isEqualTo(QueryType.OTHER); 31 | assertThat(QueryUtils.getQueryType("SELECT")).isEqualTo(QueryType.SELECT); 32 | assertThat(QueryUtils.getQueryType("select")).isEqualTo(QueryType.SELECT); 33 | assertThat(QueryUtils.getQueryType("INSERT")).isEqualTo(QueryType.INSERT); 34 | assertThat(QueryUtils.getQueryType("insert")).isEqualTo(QueryType.INSERT); 35 | assertThat(QueryUtils.getQueryType("UPDATE")).isEqualTo(QueryType.UPDATE); 36 | assertThat(QueryUtils.getQueryType("update")).isEqualTo(QueryType.UPDATE); 37 | assertThat(QueryUtils.getQueryType("DELETE")).isEqualTo(QueryType.DELETE); 38 | assertThat(QueryUtils.getQueryType("delete")).isEqualTo(QueryType.DELETE); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/listener/SingleQueryCountHolderTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import net.ttddyy.dsproxy.QueryCount; 4 | import net.ttddyy.dsproxy.QueryCountHolder; 5 | import org.junit.After; 6 | import org.junit.Test; 7 | 8 | import java.util.concurrent.atomic.AtomicReference; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | /** 13 | * @author Tadaya Tsuyukubo 14 | * @since 1.4.2 15 | */ 16 | public class SingleQueryCountHolderTest { 17 | 18 | @After 19 | public void cleanUp() { 20 | QueryCountHolder.clear(); 21 | } 22 | 23 | @Test 24 | public void getQueryCount() throws Exception { 25 | final SingleQueryCountHolder holder = new SingleQueryCountHolder(); 26 | assertThat(holder.isPopulateQueryCountHolder()).isTrue(); 27 | 28 | QueryCount mainQueryCount = holder.getOrCreateQueryCount("testDS"); 29 | QueryCount mainQueryCountHolderCount = QueryCountHolder.get("testDS"); 30 | 31 | final AtomicReference createdQueryCountReference = new AtomicReference(); 32 | final AtomicReference holderQueryCountReference = new AtomicReference(); 33 | Runnable runnable = new Runnable() { 34 | @Override 35 | public void run() { 36 | QueryCount queryCount = holder.getOrCreateQueryCount("testDS"); 37 | createdQueryCountReference.set(queryCount); 38 | holderQueryCountReference.set(QueryCountHolder.get("testDS")); 39 | } 40 | }; 41 | Thread thread = new Thread(runnable); 42 | thread.start(); 43 | thread.join(); 44 | 45 | assertThat(mainQueryCountHolderCount).isNotNull().isSameAs(mainQueryCount); 46 | assertThat(createdQueryCountReference.get()).isNotNull().isSameAs(mainQueryCount); 47 | assertThat(holderQueryCountReference.get()).isNotNull().isSameAs(mainQueryCount); 48 | 49 | } 50 | 51 | @Test 52 | public void populateQueryCountHolder() throws Exception { 53 | final SingleQueryCountHolder holder = new SingleQueryCountHolder(); 54 | holder.setPopulateQueryCountHolder(false); 55 | 56 | QueryCount mainQueryCount = holder.getOrCreateQueryCount("testDS"); 57 | assertThat(QueryCountHolder.get("testDS")).isNull(); 58 | 59 | final AtomicReference createdQueryCountReference = new AtomicReference(); 60 | final AtomicReference holderQueryCountReference = new AtomicReference(); 61 | Runnable runnable = new Runnable() { 62 | @Override 63 | public void run() { 64 | QueryCount queryCount = holder.getOrCreateQueryCount("testDS"); 65 | createdQueryCountReference.set(queryCount); 66 | holderQueryCountReference.set(QueryCountHolder.get("testDS")); 67 | } 68 | }; 69 | Thread thread = new Thread(runnable); 70 | thread.start(); 71 | thread.join(); 72 | 73 | assertThat(createdQueryCountReference.get()).isNotNull().isSameAs(mainQueryCount); 74 | assertThat(holderQueryCountReference.get()).isNull(); 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/listener/ThreadQueryCountHolderTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener; 2 | 3 | import net.ttddyy.dsproxy.QueryCount; 4 | import net.ttddyy.dsproxy.QueryCountHolder; 5 | import org.junit.After; 6 | import org.junit.Test; 7 | 8 | import java.util.concurrent.atomic.AtomicReference; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | /** 13 | * @author Tadaya Tsuyukubo 14 | * @since 1.4.2 15 | */ 16 | public class ThreadQueryCountHolderTest { 17 | 18 | @After 19 | public void tearDown() { 20 | QueryCountHolder.clear(); 21 | } 22 | 23 | @Test 24 | public void getQueryCount() throws Exception { 25 | final ThreadQueryCountHolder holder = new ThreadQueryCountHolder(); 26 | QueryCount queryCount = holder.getOrCreateQueryCount("testDS"); 27 | queryCount.incrementSuccess(); 28 | 29 | final AtomicReference queryCountReference = new AtomicReference(); 30 | Runnable runnable = new Runnable() { 31 | @Override 32 | public void run() { 33 | QueryCount queryCount = holder.getOrCreateQueryCount("testDS"); 34 | queryCount.incrementFailure(); 35 | queryCountReference.set(queryCount); 36 | } 37 | }; 38 | Thread thread = new Thread(runnable); 39 | thread.start(); 40 | thread.join(); 41 | 42 | assertThat(queryCount.getSuccess()).isEqualTo(1); 43 | assertThat(queryCount.getFailure()).isEqualTo(0); 44 | 45 | QueryCount fromThread = queryCountReference.get(); 46 | assertThat(fromThread).isNotNull().isNotSameAs(queryCount); 47 | assertThat(fromThread.getSuccess()).isEqualTo(0); 48 | assertThat(fromThread.getFailure()).isEqualTo(1); 49 | 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/listener/logging/CommonsSlowQueryListenerTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.QueryInfo; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | /** 15 | * @author Tadaya Tsuyukubo 16 | * @since 1.4.1 17 | */ 18 | public class CommonsSlowQueryListenerTest { 19 | 20 | private CommonsSlowQueryListener listener; 21 | 22 | @Before 23 | public void setup() throws Exception { 24 | this.listener = new CommonsSlowQueryListener(); 25 | this.listener.setLog(new InMemoryCommonsLog()); 26 | } 27 | 28 | @Test 29 | public void testLogMessage() throws Exception { 30 | this.listener.setThreshold(50); 31 | this.listener.setThresholdTimeUnit(TimeUnit.MILLISECONDS); 32 | 33 | ExecutionInfo executionInfo = new ExecutionInfo(); 34 | QueryInfo queryInfo = new QueryInfo(); 35 | queryInfo.setQuery("SELECT 1"); 36 | List queryInfos = new ArrayList(); 37 | queryInfos.add(queryInfo); 38 | 39 | this.listener.beforeQuery(executionInfo, queryInfos); 40 | TimeUnit.MILLISECONDS.sleep(100); 41 | this.listener.afterQuery(executionInfo, queryInfos); 42 | 43 | InMemoryCommonsLog log = (InMemoryCommonsLog) this.listener.getLog(); 44 | List messages = log.getWarnMessages(); 45 | assertThat(messages).hasSize(1); 46 | assertThat(messages.get(0)).contains("SELECT 1"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/listener/logging/InMemoryJULLogger.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.logging.Level; 8 | import java.util.logging.LogRecord; 9 | import java.util.logging.Logger; 10 | 11 | /** 12 | * JUL Logger for test use. 13 | * 14 | * @author Tadaya Tsuyukubo 15 | * @since 1.4.1 16 | */ 17 | public class InMemoryJULLogger extends Logger { 18 | 19 | private Level loggerLevel = Level.FINE; 20 | private Map> messages = new HashMap>(); 21 | 22 | { 23 | this.messages.put(Level.SEVERE, new ArrayList()); 24 | this.messages.put(Level.WARNING, new ArrayList()); 25 | this.messages.put(Level.INFO, new ArrayList()); 26 | this.messages.put(Level.CONFIG, new ArrayList()); 27 | this.messages.put(Level.FINE, new ArrayList()); 28 | this.messages.put(Level.FINER, new ArrayList()); 29 | this.messages.put(Level.FINEST, new ArrayList()); 30 | } 31 | 32 | 33 | public InMemoryJULLogger() { 34 | super("in-memory-jul-logger", null); 35 | } 36 | 37 | private InMemoryJULLogger(String name, String resourceBundleName) { 38 | super(name, resourceBundleName); 39 | } 40 | 41 | @Override 42 | public void log(LogRecord record) { 43 | String message = record.getMessage(); 44 | Level level = record.getLevel(); 45 | this.messages.get(level).add(message); 46 | } 47 | 48 | @Override 49 | public boolean isLoggable(Level level) { 50 | if (level.intValue() < loggerLevel.intValue() || loggerLevel.intValue() == Level.OFF.intValue()) { 51 | return false; 52 | } 53 | return true; 54 | } 55 | 56 | public List getSevereMessages() { 57 | return this.messages.get(Level.SEVERE); 58 | } 59 | 60 | public List getWarningMessages() { 61 | return this.messages.get(Level.WARNING); 62 | } 63 | 64 | public List getInfoMessages() { 65 | return this.messages.get(Level.INFO); 66 | } 67 | 68 | public List getConfigMessages() { 69 | return this.messages.get(Level.CONFIG); 70 | } 71 | 72 | public List getFineMessages() { 73 | return this.messages.get(Level.FINE); 74 | } 75 | 76 | public List getFinerMessages() { 77 | return this.messages.get(Level.FINER); 78 | } 79 | 80 | public List getFinestMessages() { 81 | return this.messages.get(Level.FINEST); 82 | } 83 | 84 | public void setLoggerLevel(Level loggerLevel) { 85 | this.loggerLevel = loggerLevel; 86 | } 87 | 88 | public void reset() { 89 | for (List messages : this.messages.values()) { 90 | messages.clear(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/listener/logging/JULSlowQueryListenerTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.QueryInfo; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | /** 15 | * @author Tadaya Tsuyukubo 16 | * @since 1.4.1 17 | */ 18 | public class JULSlowQueryListenerTest { 19 | 20 | private JULSlowQueryListener listener; 21 | 22 | @Before 23 | public void setup() throws Exception { 24 | this.listener = new JULSlowQueryListener(); 25 | this.listener.setLogger(new InMemoryJULLogger()); 26 | } 27 | 28 | 29 | @Test 30 | public void testLogMessage() throws Exception { 31 | this.listener.setThreshold(50); 32 | this.listener.setThresholdTimeUnit(TimeUnit.MILLISECONDS); 33 | 34 | ExecutionInfo executionInfo = new ExecutionInfo(); 35 | QueryInfo queryInfo = new QueryInfo(); 36 | queryInfo.setQuery("SELECT 1"); 37 | List queryInfos = new ArrayList(); 38 | queryInfos.add(queryInfo); 39 | 40 | this.listener.beforeQuery(executionInfo, queryInfos); 41 | TimeUnit.MILLISECONDS.sleep(300); 42 | this.listener.afterQuery(executionInfo, queryInfos); 43 | 44 | InMemoryJULLogger logger = (InMemoryJULLogger) this.listener.getLogger(); 45 | List messages = logger.getWarningMessages(); 46 | assertThat(messages).hasSize(1); 47 | assertThat(messages.get(0)).contains("SELECT 1"); 48 | } 49 | 50 | @Test 51 | public void defaultLoggerName() { 52 | JULSlowQueryListener listener = new JULSlowQueryListener(); 53 | assertThat(listener.getLogger().getName()) 54 | .as("Default logger name").isEqualTo("net.ttddyy.dsproxy.listener.logging.JULSlowQueryListener"); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/listener/logging/LoggingListenerLogLevelTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.TestUtils; 4 | import net.ttddyy.dsproxy.support.ProxyDataSource; 5 | import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.junit.runners.Parameterized; 11 | 12 | import javax.sql.DataSource; 13 | import java.sql.Connection; 14 | import java.sql.Statement; 15 | import java.util.List; 16 | 17 | import static org.assertj.core.api.Assertions.assertThat; 18 | 19 | 20 | /** 21 | * @author Tadaya Tsuyukubo 22 | */ 23 | @RunWith(Parameterized.class) 24 | public class LoggingListenerLogLevelTest { 25 | 26 | @Parameterized.Parameters 27 | public static Object[][] getLogLevelData() { 28 | return new Object[][]{ 29 | {CommonsLogLevel.DEBUG}, 30 | {CommonsLogLevel.ERROR}, 31 | {CommonsLogLevel.FATAL}, 32 | {CommonsLogLevel.INFO}, 33 | {CommonsLogLevel.TRACE}, 34 | {CommonsLogLevel.WARN}, 35 | }; 36 | } 37 | 38 | private DataSource jdbcDataSource; 39 | private CommonsLogLevel logLevel; 40 | 41 | public LoggingListenerLogLevelTest(CommonsLogLevel logLevel) { 42 | this.logLevel = logLevel; 43 | } 44 | 45 | @Before 46 | public void setup() throws Exception { 47 | // real datasource 48 | jdbcDataSource = TestUtils.getDataSourceWithData(); 49 | } 50 | 51 | @After 52 | public void teardown() throws Exception { 53 | TestUtils.shutdown(jdbcDataSource); 54 | } 55 | 56 | @Test 57 | public void testLogLevel() throws Exception { 58 | 59 | InMemoryCommonsLog log = new InMemoryCommonsLog(); 60 | log.setEnabledLogLevel(this.logLevel); 61 | 62 | CommonsQueryLoggingListener loggingListener = new CommonsQueryLoggingListener(); 63 | loggingListener.setLog(log); 64 | loggingListener.setLogLevel(logLevel); 65 | 66 | ProxyDataSource proxyDataSource = ProxyDataSourceBuilder.create(jdbcDataSource).listener(loggingListener).build(); 67 | 68 | 69 | Connection connection = proxyDataSource.getConnection(); 70 | Statement statement = connection.createStatement(); 71 | statement.executeQuery("select * from emp where id=1"); 72 | statement.executeQuery("select * from emp where id=2"); 73 | 74 | verifyMessage(log, "select * from emp where id=1", "select * from emp where id=2"); 75 | 76 | } 77 | 78 | private void verifyMessage(InMemoryCommonsLog log, String... queries) { 79 | 80 | for (CommonsLogLevel commonsLogLevel : CommonsLogLevel.values()) { 81 | List messageList = log.getMessages(commonsLogLevel); 82 | if (commonsLogLevel == this.logLevel) { 83 | assertThat(messageList).hasSize(queries.length); 84 | for (int i = 0; i < queries.length; i++) { 85 | final String query = queries[i]; 86 | assertThat(messageList.get(i)).contains(query); 87 | } 88 | } else { 89 | assertThat(messageList).isEmpty(); 90 | } 91 | } 92 | 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/listener/logging/SLF4JQueryLoggingListenerTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.ExecutionInfoBuilder; 5 | import net.ttddyy.dsproxy.QueryInfo; 6 | import net.ttddyy.dsproxy.QueryInfoBuilder; 7 | import org.junit.Test; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | /** 15 | * @author Tadaya Tsuyukubo 16 | * @since 1.3.1 17 | */ 18 | public class SLF4JQueryLoggingListenerTest { 19 | 20 | @Test 21 | public void defaultLoggerName() { 22 | SLF4JQueryLoggingListener listener = new SLF4JQueryLoggingListener(); 23 | assertThat(listener.getLogger().getName()) 24 | .as("Default logger name").isEqualTo("net.ttddyy.dsproxy.listener.logging.SLF4JQueryLoggingListener"); 25 | } 26 | 27 | @Test 28 | public void setLoggerName() { 29 | SLF4JQueryLoggingListener listener = new SLF4JQueryLoggingListener(); 30 | listener.setLogger("my.logger"); 31 | assertThat(listener.getLogger().getName()).as("Updated logger name").isEqualTo("my.logger"); 32 | } 33 | 34 | @Test 35 | public void loggingCondition() { 36 | SLF4JQueryLoggingListener listener = new SLF4JQueryLoggingListener(); 37 | InMemorySLF4JLogger logger = new InMemorySLF4JLogger(); 38 | 39 | List queryInfoList = new ArrayList(); 40 | ExecutionInfo execInfo = ExecutionInfoBuilder.create().build(); 41 | queryInfoList.add(QueryInfoBuilder.create().query("select * ").build()); 42 | 43 | 44 | // listener writes to more serious level 45 | listener.setLogLevel(SLF4JLogLevel.DEBUG); 46 | logger.setEnabledLogLevel(SLF4JLogLevel.TRACE); 47 | listener.setLogger(logger); 48 | 49 | listener.afterQuery(execInfo, queryInfoList); 50 | 51 | assertThat(logger.getTraceMessages()).isEmpty(); 52 | assertThat(logger.getDebugMessages()).hasSize(1); 53 | assertThat(logger.getInfoMessages()).isEmpty(); 54 | assertThat(logger.getWarnMessages()).isEmpty(); 55 | assertThat(logger.getErrorMessages()).isEmpty(); 56 | 57 | // listener writes to less serious level 58 | logger.reset(); 59 | listener.setLogLevel(SLF4JLogLevel.TRACE); 60 | logger.setEnabledLogLevel(SLF4JLogLevel.DEBUG); 61 | 62 | listener.afterQuery(execInfo, queryInfoList); 63 | 64 | assertThat(logger.getTraceMessages()).isEmpty(); 65 | assertThat(logger.getDebugMessages()).isEmpty(); 66 | assertThat(logger.getInfoMessages()).isEmpty(); 67 | assertThat(logger.getWarnMessages()).isEmpty(); 68 | assertThat(logger.getErrorMessages()).isEmpty(); 69 | 70 | // listener writes to same level 71 | logger.reset(); 72 | listener.setLogLevel(SLF4JLogLevel.DEBUG); 73 | logger.setEnabledLogLevel(SLF4JLogLevel.DEBUG); 74 | 75 | listener.afterQuery(execInfo, queryInfoList); 76 | 77 | assertThat(logger.getTraceMessages()).isEmpty(); 78 | assertThat(logger.getDebugMessages()).hasSize(1); 79 | assertThat(logger.getInfoMessages()).isEmpty(); 80 | assertThat(logger.getWarnMessages()).isEmpty(); 81 | assertThat(logger.getErrorMessages()).isEmpty(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/listener/logging/SLF4JSlowQueryListenerTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.listener.logging; 2 | 3 | import net.ttddyy.dsproxy.ExecutionInfo; 4 | import net.ttddyy.dsproxy.QueryInfo; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | /** 15 | * @author Tadaya Tsuyukubo 16 | * @since 1.4.1 17 | */ 18 | public class SLF4JSlowQueryListenerTest { 19 | 20 | private SLF4JSlowQueryListener listener; 21 | 22 | @Before 23 | public void setup() throws Exception { 24 | this.listener = new SLF4JSlowQueryListener(); 25 | this.listener.setLogger(new InMemorySLF4JLogger()); 26 | } 27 | 28 | 29 | @Test 30 | public void testLogMessage() throws Exception { 31 | this.listener.setThreshold(50); 32 | this.listener.setThresholdTimeUnit(TimeUnit.MILLISECONDS); 33 | 34 | ExecutionInfo executionInfo = new ExecutionInfo(); 35 | QueryInfo queryInfo = new QueryInfo(); 36 | queryInfo.setQuery("SELECT 1"); 37 | List queryInfos = new ArrayList(); 38 | queryInfos.add(queryInfo); 39 | 40 | this.listener.beforeQuery(executionInfo, queryInfos); 41 | TimeUnit.MILLISECONDS.sleep(100); 42 | this.listener.afterQuery(executionInfo, queryInfos); 43 | 44 | InMemorySLF4JLogger logger = (InMemorySLF4JLogger) this.listener.getLogger(); 45 | List messages = logger.getWarnMessages(); 46 | assertThat(messages).hasSize(1); 47 | assertThat(messages.get(0)).contains("SELECT 1"); 48 | } 49 | 50 | @Test 51 | public void defaultLoggerName() { 52 | SLF4JSlowQueryListener listener = new SLF4JSlowQueryListener(); 53 | assertThat(listener.getLogger().getName()) 54 | .as("Default logger name").isEqualTo("net.ttddyy.dsproxy.listener.logging.SLF4JSlowQueryListener"); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/proxy/DefaultConnectionIdManagerTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import org.junit.Test; 4 | 5 | import java.sql.Connection; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | import static org.mockito.Mockito.mock; 9 | 10 | /** 11 | * @author Tadaya Tsuyukubo 12 | * @since 1.4.2 13 | */ 14 | public class DefaultConnectionIdManagerTest { 15 | 16 | @Test 17 | public void getId() { 18 | DefaultConnectionIdManager idManager = new DefaultConnectionIdManager(); 19 | 20 | assertThat(idManager.getId(null)).isEqualTo("1"); 21 | assertThat(idManager.getId(null)).isEqualTo("2"); 22 | assertThat(idManager.getId(null)).isEqualTo("3"); 23 | 24 | Connection conn = mock(Connection.class); 25 | idManager = new DefaultConnectionIdManager(); 26 | 27 | assertThat(idManager.getId(conn)).isEqualTo("1"); 28 | assertThat(idManager.getId(conn)).isEqualTo("2"); 29 | assertThat(idManager.getId(conn)).isEqualTo("3"); 30 | } 31 | 32 | @Test 33 | public void getOpenConnectionIds() { 34 | DefaultConnectionIdManager idManager = new DefaultConnectionIdManager(); 35 | 36 | assertThat(idManager.getOpenConnectionIds()).isEmpty(); 37 | 38 | String id1 = idManager.getId(null); 39 | assertThat(idManager.getOpenConnectionIds()).containsExactly(id1); 40 | 41 | String id2 = idManager.getId(null); 42 | assertThat(idManager.getOpenConnectionIds()).containsExactly(id1, id2); 43 | 44 | idManager.addClosedId(id2); 45 | assertThat(idManager.getOpenConnectionIds()).containsExactly(id1); 46 | 47 | idManager.addClosedId(id1); 48 | assertThat(idManager.getOpenConnectionIds()).isEmpty(); 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/proxy/GlobalConnectionIdManagerTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.sql.Connection; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | import static org.mockito.Mockito.mock; 10 | 11 | /** 12 | * Tests for {@link GlobalConnectionIdManager}. 13 | * 14 | * @author Tadaya Tsuyukubo 15 | */ 16 | public class GlobalConnectionIdManagerTest { 17 | 18 | @Before 19 | public void setUp() { 20 | GlobalConnectionIdManager.resetId(); 21 | } 22 | 23 | @Test 24 | public void getId() { 25 | GlobalConnectionIdManager idManager1 = new GlobalConnectionIdManager(); 26 | GlobalConnectionIdManager idManager2 = new GlobalConnectionIdManager(); 27 | 28 | Connection connection = mock(Connection.class); 29 | 30 | assertThat(idManager1.getId(connection)).isEqualTo("1"); 31 | assertThat(idManager2.getId(connection)).isEqualTo("2"); 32 | assertThat(idManager1.getId(connection)).isEqualTo("3"); 33 | assertThat(idManager2.getId(connection)).isEqualTo("4"); 34 | } 35 | 36 | @Test 37 | public void getOpenConnectionIds() { 38 | GlobalConnectionIdManager idManager = new GlobalConnectionIdManager(); 39 | assertThat(idManager.getOpenConnectionIds()).isEmpty(); 40 | 41 | Connection connection = mock(Connection.class); 42 | 43 | String id1 = idManager.getId(connection); 44 | assertThat(idManager.getOpenConnectionIds()).containsExactly(id1); 45 | 46 | String id2 = idManager.getId(connection); 47 | assertThat(idManager.getOpenConnectionIds()).containsExactly(id1, id2); 48 | 49 | idManager.addClosedId(id2); 50 | assertThat(idManager.getOpenConnectionIds()).containsExactly(id1); 51 | 52 | idManager.addClosedId(id1); 53 | assertThat(idManager.getOpenConnectionIds()).isEmpty(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/proxy/JdkJdbcProxyFactoryTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | import net.ttddyy.dsproxy.proxy.jdk.JdkJdbcProxyFactory; 5 | import org.junit.Test; 6 | 7 | import javax.sql.DataSource; 8 | import java.sql.CallableStatement; 9 | import java.sql.Connection; 10 | import java.sql.PreparedStatement; 11 | import java.sql.Statement; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | import static org.mockito.Mockito.mock; 15 | 16 | /** 17 | * @author Tadaya Tsuyukubo 18 | */ 19 | public class JdkJdbcProxyFactoryTest { 20 | 21 | private JdkJdbcProxyFactory factory = new JdkJdbcProxyFactory(); 22 | 23 | @Test 24 | public void testCreateConnection() { 25 | Connection conn = mock(Connection.class); 26 | ProxyConfig proxyConfig = ProxyConfig.Builder.create().build(); 27 | 28 | Connection result = factory.createConnection(conn, getConnectionInfo(), proxyConfig); 29 | 30 | assertThat(result).isNotNull().isNotSameAs(conn).isInstanceOf(ProxyJdbcObject.class); 31 | } 32 | 33 | @Test 34 | public void testCreateStatement() { 35 | Statement stmt = mock(Statement.class); 36 | ProxyConfig proxyConfig = ProxyConfig.Builder.create().build(); 37 | 38 | Statement result = factory.createStatement(stmt, getConnectionInfo(), null, proxyConfig); 39 | 40 | assertThat(result).isNotNull().isNotSameAs(stmt).isInstanceOf(ProxyJdbcObject.class); 41 | } 42 | 43 | @Test 44 | public void testCreatePreparedStatement() { 45 | PreparedStatement ps = mock(PreparedStatement.class); 46 | 47 | ProxyConfig proxyConfig = ProxyConfig.Builder.create().build(); 48 | 49 | PreparedStatement result = factory.createPreparedStatement(ps, "my-query", getConnectionInfo(), null, proxyConfig, false); 50 | 51 | assertThat(result).isNotNull().isNotSameAs(ps).isInstanceOf(ProxyJdbcObject.class); 52 | } 53 | 54 | @Test 55 | public void testCreateCallableStatement() { 56 | CallableStatement cs = mock(CallableStatement.class); 57 | 58 | ProxyConfig proxyConfig = ProxyConfig.Builder.create().build(); 59 | 60 | CallableStatement result = factory.createCallableStatement(cs, "my-query", getConnectionInfo(), null, proxyConfig); 61 | 62 | assertThat(result).isNotNull().isNotSameAs(cs).isInstanceOf(ProxyJdbcObject.class); 63 | } 64 | 65 | @Test 66 | public void testCreateDataSource() { 67 | DataSource ds = mock(DataSource.class); 68 | ProxyConfig proxyConfig = ProxyConfig.Builder.create().dataSourceName("my-ds").build(); 69 | 70 | DataSource result = factory.createDataSource(ds, proxyConfig); 71 | 72 | assertThat(result).isNotNull().isNotSameAs(ds).isInstanceOf(ProxyJdbcObject.class); 73 | } 74 | 75 | 76 | private ConnectionInfo getConnectionInfo() { 77 | ConnectionInfo connectionInfo = new ConnectionInfo(); 78 | connectionInfo.setDataSourceName("my-ds"); 79 | return connectionInfo; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/proxy/NativeJdbcExtractUtilsTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import net.ttddyy.dsproxy.ConnectionInfo; 4 | import net.ttddyy.dsproxy.proxy.jdk.JdkJdbcProxyFactory; 5 | import org.junit.Test; 6 | 7 | import java.sql.CallableStatement; 8 | import java.sql.Connection; 9 | import java.sql.PreparedStatement; 10 | import java.sql.Statement; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | import static org.mockito.Mockito.mock; 14 | 15 | /** 16 | * @author Tadaya Tsuyukubo 17 | */ 18 | public class NativeJdbcExtractUtilsTest { 19 | 20 | @Test 21 | public void testGetConnection() { 22 | Connection source = mock(Connection.class); 23 | Connection proxy = new JdkJdbcProxyFactory().createConnection(source, getConnectionInfo(), ProxyConfig.Builder.create().build()); 24 | 25 | // check proxy 26 | Connection result = NativeJdbcExtractUtils.getConnection(proxy); 27 | assertThat(result).isSameAs(source); 28 | 29 | // check non-proxy 30 | result = NativeJdbcExtractUtils.getConnection(source); 31 | assertThat(result).isSameAs(source); 32 | } 33 | 34 | @Test 35 | public void testGetStatement() { 36 | Statement source = mock(Statement.class); 37 | Statement proxy = new JdkJdbcProxyFactory().createStatement(source, getConnectionInfo(), null, ProxyConfig.Builder.create().build()); 38 | 39 | // check proxy 40 | Statement result = NativeJdbcExtractUtils.getStatement(proxy); 41 | assertThat(result).isSameAs(source); 42 | 43 | // check non-proxy 44 | result = NativeJdbcExtractUtils.getStatement(source); 45 | assertThat(result).isSameAs(source); 46 | } 47 | 48 | @Test 49 | public void testGetPreparedStatement() { 50 | PreparedStatement source = mock(PreparedStatement.class); 51 | PreparedStatement proxy = new JdkJdbcProxyFactory().createPreparedStatement(source, null, getConnectionInfo(), null, ProxyConfig.Builder.create().build(), false); 52 | 53 | // check proxy 54 | PreparedStatement result = NativeJdbcExtractUtils.getPreparedStatement(proxy); 55 | assertThat(result).isSameAs(source); 56 | 57 | // check non-proxy 58 | result = NativeJdbcExtractUtils.getPreparedStatement(source); 59 | assertThat(result).isSameAs(source); 60 | } 61 | 62 | @Test 63 | public void testGetCallableStatement() { 64 | CallableStatement source = mock(CallableStatement.class); 65 | CallableStatement proxy = new JdkJdbcProxyFactory().createCallableStatement(source, null, getConnectionInfo(), null, ProxyConfig.Builder.create().build()); 66 | 67 | // check proxy 68 | CallableStatement result = NativeJdbcExtractUtils.getCallableStatement(proxy); 69 | assertThat(result).isSameAs(source); 70 | 71 | // check non-proxy 72 | result = NativeJdbcExtractUtils.getCallableStatement(source); 73 | assertThat(result).isSameAs(source); 74 | } 75 | 76 | private ConnectionInfo getConnectionInfo() { 77 | ConnectionInfo connectionInfo = new ConnectionInfo(); 78 | connectionInfo.setDataSourceName("myDs"); 79 | return connectionInfo; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/proxy/ObjectArrayUtilsTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.proxy; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.junit.runners.Parameterized; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | /** 10 | * @author Tadaya Tsuyukubo 11 | */ 12 | @RunWith(Parameterized.class) 13 | public class ObjectArrayUtilsTest { 14 | 15 | @Parameterized.Parameters 16 | public static Object[][] getIsFirstArgStringData() { 17 | return new Object[][]{ 18 | // expected, input string 19 | {true, new Object[]{"str"}}, 20 | {true, new Object[]{"str", "str"}}, 21 | {true, new Object[]{"", "str"}}, 22 | {false, new Object[]{}}, // empty 23 | {false, new Object[]{null, "str"}}, 24 | {false, new Object[]{1, "str"}}, 25 | {false, new Object[]{1L, "str"}}, 26 | {false, new Object[]{1.0, "str"}}, 27 | {false, new Object[]{'c', "str"}}, // character 28 | {false, new Object[]{new Object(), "str"}}, 29 | }; 30 | } 31 | 32 | private boolean expected; 33 | private Object[] inputArray; 34 | 35 | public ObjectArrayUtilsTest(boolean expected, Object[] inputArray) { 36 | this.expected = expected; 37 | this.inputArray = inputArray; 38 | } 39 | 40 | @Test 41 | public void testIsFirstArgString() { 42 | boolean actual = ObjectArrayUtils.isFirstArgString(this.inputArray); 43 | assertThat(actual).isEqualTo(this.expected); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/support/DefaultQueryCountLogEntryCreatorTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.support; 2 | 3 | import net.ttddyy.dsproxy.QueryCount; 4 | import org.junit.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | /** 9 | * @author Tadaya Tsuyukubo 10 | * @since 1.3 11 | */ 12 | public class DefaultQueryCountLogEntryCreatorTest { 13 | 14 | @Test 15 | public void logFormat() { 16 | QueryCount queryCount = new QueryCount(); 17 | queryCount.setTime(1); 18 | queryCount.setTotal(2); 19 | queryCount.setSuccess(3); 20 | queryCount.setFailure(4); 21 | queryCount.setSelect(5); 22 | queryCount.setInsert(6); 23 | queryCount.setUpdate(7); 24 | queryCount.setDelete(8); 25 | queryCount.setOther(9); 26 | queryCount.setStatement(10); 27 | queryCount.setPrepared(11); 28 | queryCount.setCallable(12); 29 | 30 | String log = new DefaultQueryCountLogEntryCreator().getLogMessage("DS", queryCount); 31 | assertThat(log).isEqualTo("Name:DS, Time:1, Total:2, Success:3, Failure:4, Select:5, Insert:6, Update:7, Delete:8, Other:9, Statement:10, Prepared:11, Callable:12"); 32 | 33 | String jsonLog = new DefaultQueryCountLogEntryCreator().getLogMessageAsJson("DS", queryCount); 34 | assertThat(jsonLog).isEqualTo("{\"name\":\"DS\", \"time\":1, \"total\":2, \"success\":3, \"failure\":4, \"select\":5, \"insert\":6, \"update\":7, \"delete\":8, \"other\":9, \"statement\":10, \"prepared\":11, \"callable\":12}"); 35 | } 36 | 37 | @Test 38 | public void logFormatWithNoName() { 39 | QueryCount queryCount = new QueryCount(); 40 | 41 | String log = new DefaultQueryCountLogEntryCreator().getLogMessage(null, queryCount); 42 | assertThat(log).isEqualTo("Name:, Time:0, Total:0, Success:0, Failure:0, Select:0, Insert:0, Update:0, Delete:0, Other:0, Statement:0, Prepared:0, Callable:0"); 43 | 44 | String jsonLog = new DefaultQueryCountLogEntryCreator().getLogMessageAsJson(null, queryCount); 45 | assertThat(jsonLog).isEqualTo("{\"name\":null, \"time\":0, \"total\":0, \"success\":0, \"failure\":0, \"select\":0, \"insert\":0, \"update\":0, \"delete\":0, \"other\":0, \"statement\":0, \"prepared\":0, \"callable\":0}"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/net/ttddyy/dsproxy/transform/ParameterReplacerTest.java: -------------------------------------------------------------------------------- 1 | package net.ttddyy.dsproxy.transform; 2 | 3 | import net.ttddyy.dsproxy.proxy.ParameterKey; 4 | import net.ttddyy.dsproxy.proxy.ParameterSetOperation; 5 | import org.junit.Test; 6 | 7 | import java.lang.reflect.Method; 8 | import java.util.LinkedHashMap; 9 | import java.util.Map; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | 14 | /** 15 | * @author Tadaya Tsuyukubo 16 | */ 17 | public class ParameterReplacerTest { 18 | 19 | @Test 20 | public void testParamReplacement() { 21 | 22 | Method method = null; 23 | 24 | Map input = new LinkedHashMap(); 25 | input.put(new ParameterKey(1), new ParameterSetOperation(method, new Object[]{1, "value-1"})); 26 | input.put(new ParameterKey(2), new ParameterSetOperation(method, new Object[]{2, "value-2"})); 27 | input.put(new ParameterKey("foo"), new ParameterSetOperation(method, new Object[]{"foo", "value-foo"})); 28 | input.put(new ParameterKey("bar"), new ParameterSetOperation(method, new Object[]{"bar", "value-bar"})); 29 | 30 | ParameterReplacer replacer = new ParameterReplacer(input); 31 | replacer.setString(1, "replaced-1"); 32 | replacer.setString("foo", "replaced-foo"); 33 | 34 | Map params = replacer.getModifiedParameters(); 35 | assertThat(params.keySet()).hasSize(4); 36 | assertThat(params.keySet()).containsExactly(new ParameterKey(1), new ParameterKey(2), new ParameterKey("foo"), new ParameterKey("bar")); 37 | 38 | assertThat((String) params.get(new ParameterKey(1)).getArgs()[1]).isEqualTo("replaced-1"); 39 | assertThat((String) params.get(new ParameterKey(2)).getArgs()[1]).isEqualTo("value-2"); 40 | assertThat((String) params.get(new ParameterKey("foo")).getArgs()[1]).isEqualTo("replaced-foo"); 41 | assertThat((String) params.get(new ParameterKey("bar")).getArgs()[1]).isEqualTo("value-bar"); 42 | 43 | } 44 | 45 | @Test 46 | public void testClearParamAndReplace() { 47 | 48 | Method method = null; 49 | 50 | Map input = new LinkedHashMap(); 51 | input.put(new ParameterKey(1), new ParameterSetOperation(method, new Object[]{1, "value-1"})); 52 | input.put(new ParameterKey(2), new ParameterSetOperation(method, new Object[]{2, "value-2"})); 53 | input.put(new ParameterKey("foo"), new ParameterSetOperation(method, new Object[]{"foo", "value-foo"})); 54 | input.put(new ParameterKey("bar"), new ParameterSetOperation(method, new Object[]{"bar", "value-bar"})); 55 | 56 | ParameterReplacer replacer = new ParameterReplacer(input); 57 | replacer.clearParameters(); 58 | replacer.setString(1, "replaced-1"); 59 | replacer.setString("foo", "replaced-foo"); 60 | 61 | Map params = replacer.getModifiedParameters(); 62 | assertThat(params.keySet()).containsExactly(new ParameterKey(1), new ParameterKey("foo")); 63 | 64 | assertThat((String) params.get(new ParameterKey(1)).getArgs()[1]).isEqualTo("replaced-1"); 65 | assertThat((String) params.get(new ParameterKey("foo")).getArgs()[1]).isEqualTo("replaced-foo"); 66 | } 67 | 68 | } 69 | --------------------------------------------------------------------------------