View Javadoc
1   package com.atlassian.activeobjects.spi;
2   
3   import java.lang.reflect.InvocationHandler;
4   import java.lang.reflect.InvocationTargetException;
5   import java.lang.reflect.Method;
6   import java.lang.reflect.Proxy;
7   import java.sql.Connection;
8   import java.sql.SQLException;
9   
10  import static com.google.common.base.Preconditions.checkNotNull;
11  
12  /**
13   * <p>A connection that can't be closed.</p>
14   * <p>All calls to Active Objects must happen within a transaction. For this transactions to be successful, we can't let
15   * ActiveObjects close the connection in the middle of it.</p>
16   */
17  public final class ConnectionHandler implements InvocationHandler {
18      private final Connection delegate;
19      private final Closeable closeable;
20  
21      public ConnectionHandler(Connection delegate, Closeable closeable) {
22          this.delegate = checkNotNull(delegate);
23          this.closeable = checkNotNull(closeable);
24      }
25  
26      @Override
27      public Object invoke(Object proxy, Method method, Object[] args) throws SQLException {
28          if (isCloseMethod(method)) {
29              closeable.close();
30              return Void.TYPE;
31          }
32  
33          return delegate(method, args);
34      }
35  
36      private Object delegate(Method method, Object[] args) throws SQLException {
37          try {
38              return method.invoke(delegate, args);
39          } catch (IllegalAccessException e) {
40              throw new IllegalStateException(e);
41          } catch (InvocationTargetException e) {
42              Throwable cause = e.getCause();
43  
44              if (cause instanceof SQLException) {
45                  throw (SQLException) cause;
46              }
47  
48              if (cause instanceof RuntimeException) {
49                  throw (RuntimeException) cause;
50              }
51  
52              if (cause instanceof Error) {
53                  throw (Error) cause;
54              }
55  
56              throw new RuntimeException("Unexpected checked exception", cause);
57          }
58      }
59  
60      public static Connection newInstance(Connection c) {
61          return newInstance(c, new Closeable() {
62              @Override
63              public void close() throws SQLException {
64              }
65          });
66      }
67  
68      public static Connection newInstance(Connection c, Closeable closeable) {
69          return (Connection) Proxy.newProxyInstance(
70                  ConnectionHandler.class.getClassLoader(),
71                  new Class[]{Connection.class},
72                  new ConnectionHandler(c, closeable));
73      }
74  
75      private static boolean isCloseMethod(Method method) {
76          return method.getName().equals("close")
77                  && method.getParameterTypes().length == 0;
78      }
79  
80      public static interface Closeable {
81          void close() throws SQLException;
82      }
83  }
84