1 package com.atlassian.activeobjects.tx;
2
3 import com.atlassian.activeobjects.external.ActiveObjects;
4 import com.atlassian.sal.api.transaction.TransactionCallback;
5
6 import java.lang.annotation.Annotation;
7 import java.lang.reflect.AnnotatedElement;
8 import java.lang.reflect.InvocationHandler;
9 import java.lang.reflect.InvocationTargetException;
10 import java.lang.reflect.Method;
11 import java.lang.reflect.Proxy;
12
13 import static com.google.common.base.Preconditions.checkNotNull;
14
15
16
17
18 public final class TransactionalProxy implements InvocationHandler {
19 private static final Class<? extends Annotation> ANNOTATION_CLASS = Transactional.class;
20
21 private final ActiveObjects ao;
22 private final Object obj;
23
24 public TransactionalProxy(ActiveObjects ao, Object obj) {
25 this.ao = ao;
26 this.obj = obj;
27 }
28
29 public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
30 if (isAnnotated(method)) {
31 return invokeInTransaction(method, args);
32 } else {
33 return invoke(method, args);
34 }
35 }
36
37 private Object invokeInTransaction(final Method method, final Object[] args) throws Throwable {
38 try {
39 return executeInTransaction(method, args);
40 } catch (TransactionalException e) {
41 throw e.getThrowable();
42 }
43 }
44
45 private Object executeInTransaction(final Method method, final Object[] args) {
46 return ao.executeInTransaction(new TransactionCallback<Object>() {
47 public Object doInTransaction() {
48 try {
49 return invoke(method, args);
50 } catch (IllegalAccessException e) {
51 throw new TransactionalException(e);
52 } catch (InvocationTargetException e) {
53 throw new TransactionalException(e);
54 }
55 }
56 });
57 }
58
59 private Object invoke(Method method, Object[] args) throws IllegalAccessException, InvocationTargetException {
60 return method.invoke(obj, args);
61 }
62
63
64
65
66
67
68
69
70
71 public static Object transactional(ActiveObjects ao, Object o) {
72 checkNotNull(o);
73 final Class c = o.getClass();
74 return Proxy.newProxyInstance(c.getClassLoader(), c.getInterfaces(), new TransactionalProxy(ao, o));
75 }
76
77 static boolean isAnnotated(Method method) {
78 return method != null && (isAnnotationPresent(method) || isAnnotationPresent(method.getDeclaringClass()));
79 }
80
81
82
83
84
85
86
87 public static boolean isAnnotated(Class c) {
88 if (c != null) {
89 if (c.isInterface()) {
90 if (isAnnotationPresent(c)) {
91 return true;
92 }
93 for (Method method : c.getMethods()) {
94 if (isAnnotated(method)) {
95 return true;
96 }
97 }
98 }
99
100 for (Class ifce : c.getInterfaces()) {
101 if (isAnnotated(ifce)) {
102 return true;
103 }
104 }
105 }
106 return false;
107 }
108
109 private static boolean isAnnotationPresent(AnnotatedElement e) {
110 return e.isAnnotationPresent(ANNOTATION_CLASS);
111 }
112
113 private static final class TransactionalException extends RuntimeException {
114 public TransactionalException(Throwable cause) {
115 super(cause);
116 }
117
118 public Throwable getThrowable() {
119 final Throwable cause = getCause();
120 if (cause instanceof InvocationTargetException) {
121 return cause.getCause();
122 } else {
123 return cause;
124 }
125 }
126 }
127 }