PageRenderTime 37ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/messaging/src/main/java/org/axonframework/common/jdbc/ConnectionWrapperFactory.java

http://github.com/AxonFramework/AxonFramework
Java | 165 lines | 88 code | 14 blank | 63 comment | 37 complexity | 3465d603f0cf289a64b8f991ff88d278 MD5 | raw file
  1. /*
  2. * Copyright (c) 2010-2018. Axon Framework
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.axonframework.common.jdbc;
  17. import java.lang.reflect.InvocationTargetException;
  18. import java.lang.reflect.Method;
  19. import java.lang.reflect.Proxy;
  20. import java.sql.Connection;
  21. import java.sql.SQLException;
  22. /**
  23. * Factory for creating wrappers around a Connection, allowing one to override the behavior of the {@link
  24. * java.sql.Connection#close()} method.
  25. *
  26. * @author Allard Buijze
  27. * @since 2.2
  28. */
  29. public abstract class ConnectionWrapperFactory {
  30. private ConnectionWrapperFactory() {
  31. }
  32. /**
  33. * Wrap the given {@code connection}, creating a Proxy with an additional {@code wrapperInterface}
  34. * (implemented by given {@code wrapperHandler}). Calls to the close method are forwarded to the given
  35. * {@code closeHandler}.
  36. * <p/>
  37. * Note that all invocations on methods declared on the {@code wrapperInterface} (including equals, hashCode)
  38. * are forwarded to the {@code wrapperHandler}.
  39. *
  40. * @param connection The connection to wrap
  41. * @param wrapperInterface The additional interface to implement
  42. * @param wrapperHandler The implementation for the additional interface
  43. * @param closeHandler The handler to redirect close invocations to
  44. * @param <I> The type of additional interface for the wrapper to implement
  45. * @return a wrapped Connection
  46. */
  47. public static <I> Connection wrap(final Connection connection, final Class<I> wrapperInterface,
  48. final I wrapperHandler,
  49. final ConnectionCloseHandler closeHandler) {
  50. return (Connection) Proxy.newProxyInstance(wrapperHandler.getClass().getClassLoader(),
  51. new Class[]{Connection.class, wrapperInterface},
  52. (proxy, method, args) -> {
  53. if ("equals".equals(method.getName()) && args != null
  54. && args.length == 1) {
  55. return proxy == args[0];
  56. } else if ("hashCode".equals(
  57. method.getName()) && isEmpty(args)) {
  58. return connection.hashCode();
  59. } else if (method.getDeclaringClass().isAssignableFrom(
  60. wrapperInterface)) {
  61. return invokeMethodAndUnwrapNestedException(
  62. method, wrapperHandler,
  63. args);
  64. } else if ("close".equals(method.getName())
  65. && isEmpty(args)) {
  66. closeHandler.close(connection);
  67. return null;
  68. } else if ("commit".equals(method.getName())
  69. && isEmpty(args)) {
  70. closeHandler.commit(connection);
  71. return null;
  72. } else {
  73. return invokeMethodAndUnwrapNestedException(method,
  74. connection,
  75. args);
  76. }
  77. });
  78. }
  79. /**
  80. * Wrap the given {@code connection}, creating a Proxy with an additional {@code wrapperInterface}
  81. * (implemented by given {@code wrapperHandler}). Calls to the close method are forwarded to the given
  82. * {@code closeHandler}.
  83. *
  84. * @param connection The connection to wrap
  85. * @param closeHandler The handler to redirect close invocations to
  86. * @return a wrapped Connection
  87. */
  88. public static Connection wrap(final Connection connection, final ConnectionCloseHandler closeHandler) {
  89. return (Connection) Proxy.newProxyInstance(closeHandler.getClass().getClassLoader(),
  90. new Class[]{Connection.class}, (proxy, method, args) -> {
  91. if ("equals".equals(method.getName()) && args != null
  92. && args.length == 1) {
  93. return proxy == args[0];
  94. } else if ("hashCode".equals(
  95. method.getName()) && isEmpty(args)) {
  96. return connection.hashCode();
  97. } else if ("close".equals(method.getName())
  98. && isEmpty(args)) {
  99. closeHandler.close(connection);
  100. return null;
  101. } else if ("commit".equals(method.getName())
  102. && isEmpty(args)) {
  103. closeHandler.commit(connection);
  104. return null;
  105. } else {
  106. return invokeMethodAndUnwrapNestedException(method, connection, args);
  107. }
  108. });
  109. }
  110. private static boolean isEmpty(Object[] array) {
  111. return array == null || array.length == 0;
  112. }
  113. private static Object invokeMethodAndUnwrapNestedException(Method method, Object objectToInvokeMethodOn,
  114. Object[] args)
  115. throws Throwable {
  116. try {
  117. return method.invoke(objectToInvokeMethodOn, args);
  118. } catch (InvocationTargetException e) {
  119. throw e.getCause();
  120. }
  121. }
  122. /**
  123. * Interface defining an operation to close the wrapped connection
  124. */
  125. public interface ConnectionCloseHandler {
  126. /**
  127. * Close the given {@code connection}, which was wrapped by the ConnectionWrapperFactory.
  128. *
  129. * @param connection the wrapped connection to close
  130. */
  131. void close(Connection connection);
  132. /**
  133. * Commits the underlying transaction
  134. *
  135. * @param connection the wrapped connection to commit
  136. * @throws java.sql.SQLException when an error occurs while committing the connection
  137. */
  138. void commit(Connection connection) throws SQLException;
  139. }
  140. /**
  141. * Implementation of ConnectionCloseHandler that does nothing on close or commit.
  142. */
  143. public static class NoOpCloseHandler implements ConnectionCloseHandler {
  144. @Override
  145. public void close(Connection connection) {
  146. }
  147. @Override
  148. public void commit(Connection connection) {
  149. }
  150. }
  151. }