fork download
  1. import java.sql.Connection;
  2. import java.sql.DriverManager;
  3. import java.util.Collections;
  4. import java.util.Map;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.LinkedList;
  8. import java.util.concurrent.atomic.AtomicInteger;
  9.  
  10. import java.lang.reflect.Field;
  11. import java.lang.reflect.Modifier;
  12.  
  13. import org.aspectj.lang.Signature;
  14.  
  15. public aspect LoanAndReturnAspect {
  16. private ThreadLocal<Integer> depth = new ThreadLocal() {
  17. @Override
  18. protected Integer initialValue() {
  19. return 0;
  20. }
  21. };
  22. private ThreadLocal<Map<Connection, Integer>> possessedConnection = new ThreadLocal() {
  23. @Override
  24. protected Map<Connection, Integer> initialValue() {
  25. return new HashMap<Connection, Integer>();
  26. }
  27. };
  28. private Map<Connection, AtomicInteger> refCounts =
  29. Collections.synchronizedMap(new HashMap<Connection, AtomicInteger>());
  30. private Map<Connection, Thread> departments =
  31. Collections.synchronizedMap(new HashMap<Connection, Thread>());
  32.  
  33. pointcut loanConnection() :
  34. call(Connection DriverManager.getConnection(..)) && !within(LoanAndReturnAspect); // 註1
  35. pointcut retainConnection(Connection conn, Object theObj) :
  36. set(!static Connection+ *.*) && target(theObj) && args(conn) && !within(LoanAndReturnAspect);
  37. pointcut retainConnectionStatic(Connection conn) :
  38. set(static Connection+ *.*) && args(conn) && !within(LoanAndReturnAspect);
  39. pointcut checkPoint() :
  40. execution(* *.*(..)) && !within(LoanAndReturnAspect); // 註2
  41. pointcut releaseConnection(Connection conn) :
  42. call(void Connection+.close()) && target(conn) && !within(LoanAndReturnAspect);
  43.  
  44. after() returning(Connection conn) : loanConnection() { // 註3
  45. System.out.printf("Loan a connection: %s%n", conn);
  46. possessedConnection.get().put(conn, depth.get());
  47. departments.put(conn, Thread.currentThread());
  48. refCounts.put(conn, new AtomicInteger(0));
  49. }
  50.  
  51. Object around() : checkPoint() {
  52. Signature sig = thisJoinPoint.getSignature();
  53. logMethod(sig, true);
  54. try {
  55. depth.set(depth.get() + 1);
  56. return proceed();
  57. }
  58. finally {
  59. depth.set(depth.get() - 1);
  60. tryToReturnConnection();
  61. logMethod(sig, false);
  62. }
  63. }
  64.  
  65. after(Connection conn) : releaseConnection(conn) && !cflowbelow(execution(void Connection+.close())) {
  66. System.out.printf("[Manually close] %s%n", conn);
  67. refCounts.remove(conn);
  68. Thread department = departments.get(conn);
  69. if (department == Thread.currentThread())
  70. possessedConnection.get().remove(conn);
  71. departments.remove(conn);
  72. }
  73.  
  74. before(Connection newConn, Object theObj) : retainConnection(newConn, theObj) {
  75. Connection oldConn = (Connection) getFieldValue(theObj, thisJoinPoint.getSignature().getName());
  76. if (isTracking(oldConn))
  77. release(oldConn);
  78. if (isTracking(newConn))
  79. retain(newConn);
  80. }
  81.  
  82. before(Connection newConn) : retainConnectionStatic(newConn) {
  83. Signature sig = thisJoinPoint.getSignature();
  84. Connection oldConn = (Connection) getStaticFieldValue(sig.getDeclaringType(), sig.getName());
  85. if (isTracking(oldConn))
  86. release(oldConn);
  87. if (isTracking(newConn))
  88. retain(newConn);
  89. }
  90.  
  91. private boolean isTracking(Connection conn) {
  92. return refCounts.containsKey(conn);
  93. }
  94.  
  95. private void retain(Connection conn) {
  96. int refCount = refCounts.get(conn).incrementAndGet();
  97. System.out.printf("[Retain] %s[%d]%n", conn, refCount);
  98. }
  99.  
  100. private void release(Connection conn) {
  101. int refCount = refCounts.get(conn).decrementAndGet();
  102. System.out.printf("[Release] %s[%d]%n", conn, refCount);
  103. if (refCount < 1 && !departments.get(conn).isAlive()) {
  104. returnConnection(conn, false);
  105. }
  106. }
  107.  
  108. private void tryToReturnConnection() {
  109. int stackDepth = depth.get();
  110. Map<Connection, Integer> connTable = possessedConnection.get();
  111. List<Connection> willReturn = new LinkedList<Connection>();
  112. for (Connection conn : connTable.keySet()) {
  113. if (!isTracking(conn)) continue;
  114. int bornDepth = connTable.get(conn);
  115. if (bornDepth > stackDepth && refCounts.get(conn).intValue() < 1)
  116. willReturn.add(conn);
  117. }
  118.  
  119. for (Connection conn : willReturn) {
  120. returnConnection(conn, true);
  121. }
  122. }
  123.  
  124. private void returnConnection(Connection conn, boolean inOriginalDepartment) {
  125. System.out.printf("[Auto close] %s%n", conn);
  126. try {
  127. conn.close();
  128. }
  129. catch (java.sql.SQLException e) {
  130. }
  131. refCounts.remove(conn);
  132. departments.remove(conn);
  133. if (inOriginalDepartment) {
  134. possessedConnection.get().remove(conn);
  135. }
  136. }
  137.  
  138. private void logMethod(Signature sig, boolean isEntering) {
  139. int stackDepth = depth.get();
  140. System.out.printf("%s<%s%s:%s>%n",
  141. computeIndentation(stackDepth),
  142. isEntering? "" : "/",
  143. Thread.currentThread().getName(), sig);
  144. }
  145.  
  146. private String computeIndentation(int level) {
  147. StringBuilder buf = new StringBuilder();
  148. for (int i = 0; i < level; ++i)
  149. buf.append(" ");
  150. return buf.toString();
  151. }
  152.  
  153. private Object getFieldValue(Object obj, String name) {
  154. Field field = null;
  155. try {
  156. field = obj.getClass().getDeclaredField(name);
  157. if (Modifier.isStatic(field.getModifiers()))
  158. field = null;
  159. }
  160. catch (Exception e) {
  161. }
  162. if (field == null) {
  163. try {
  164. field = obj.getClass().getField(name);
  165. if (Modifier.isStatic(field.getModifiers()))
  166. field = null;
  167. }
  168. catch (Exception e1) {
  169. }
  170. }
  171. if (field == null)
  172. throw new RuntimeException(String.format("can't find instance field named: %s in class: %s", name, obj.getClass()));
  173. field.setAccessible(true);
  174. try {
  175. return field.get(obj);
  176. }
  177. catch (Exception e) {
  178. throw new RuntimeException(e);
  179. }
  180. }
  181.  
  182. private Object getStaticFieldValue(Class<?> klass, String name) {
  183. Field field = null;
  184. try {
  185. field = klass.getDeclaredField(name);
  186. if (!Modifier.isStatic(field.getModifiers()))
  187. field = null;
  188. }
  189. catch (Exception e) {
  190. }
  191. if (field == null) {
  192. try {
  193. field = klass.getField(name);
  194. if (!Modifier.isStatic(field.getModifiers()))
  195. field = null;
  196. }
  197. catch (Exception e1) {
  198. }
  199. }
  200. if (field == null)
  201. throw new RuntimeException(String.format("can't find static field named: %s in class: %s", name, klass));
  202. field.setAccessible(true);
  203. try {
  204. return field.get(klass);
  205. }
  206. catch (Exception e) {
  207. throw new RuntimeException(e);
  208. }
  209. }
  210. }
  211.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty