1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.uif.util;
17
18 import java.lang.reflect.InvocationHandler;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Proxy;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.WeakHashMap;
27
28 import org.kuali.rice.krad.datadictionary.Copyable;
29
30
31
32
33
34
35
36
37
38
39
40
41
42 public class DelayedCopyableHandler implements InvocationHandler {
43
44 private static final String COPY = "copy";
45
46 private final Copyable original;
47 private Copyable copy;
48
49 DelayedCopyableHandler(Copyable original) {
50 this.original = original;
51 }
52
53
54
55
56
57
58
59
60
61
62
63
64
65 @Override
66 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
67 String methodName = method.getName();
68 Class<?> returnType = method.getReturnType();
69 boolean atomic = copy == null && (COPY.equals(methodName) ||
70 ((methodName.startsWith("get") || methodName.startsWith("is"))
71 && !Copyable.class.isAssignableFrom(returnType)
72 && !List.class.isAssignableFrom(returnType)
73 && !Map.class.isAssignableFrom(returnType)
74 && !returnType.isArray()));
75 ProcessLogger.ntrace("delay-" + (copy != null ? "dup" : atomic ? "atomic" : "copy") +
76 ":", ":" + methodName + ":" + original.getClass().getSimpleName(), 1000);
77
78 if (copy == null && !atomic) {
79 copy = CopyUtils.copy(original);
80 }
81
82 try {
83 return method.invoke(copy == null ? original : copy, args);
84 } catch (InvocationTargetException e) {
85 if (e.getCause() != null) {
86 throw e.getCause();
87 } else {
88 throw e;
89 }
90 }
91 }
92
93
94
95
96
97
98
99
100 static <T> T unwrap(T source) {
101 if (!(source instanceof Copyable)) {
102 return source;
103 }
104
105 Class<?> sourceClass = source.getClass();
106 if (!Proxy.isProxyClass(sourceClass)) {
107 return source;
108 }
109
110 InvocationHandler handler = Proxy.getInvocationHandler(source);
111 if (!(handler instanceof DelayedCopyableHandler)) {
112 return source;
113 }
114
115 DelayedCopyableHandler sourceHandler = (DelayedCopyableHandler) handler;
116 if (sourceHandler.copy == null) {
117 sourceHandler.copy = CopyUtils.copy(sourceHandler.original);
118 }
119
120 @SuppressWarnings("unchecked")
121 T rv = (T) sourceHandler.copy;
122 return unwrap(rv);
123 }
124
125
126
127
128
129
130
131 public static boolean isPendingDelayedCopy(Copyable source) {
132 Class<?> sourceClass = source.getClass();
133
134
135 if (Proxy.isProxyClass(sourceClass)) {
136 InvocationHandler handler = Proxy.getInvocationHandler(source);
137 if (handler instanceof DelayedCopyableHandler) {
138 DelayedCopyableHandler sourceHandler = (DelayedCopyableHandler) handler;
139 return sourceHandler.copy == null;
140 }
141 }
142
143 return false;
144 }
145
146
147
148
149
150
151 public static Copyable getDelayedCopy(Copyable source) {
152 Class<?> sourceClass = source.getClass();
153
154
155 if (Proxy.isProxyClass(sourceClass)) {
156 InvocationHandler handler = Proxy.getInvocationHandler(source);
157 if (handler instanceof DelayedCopyableHandler) {
158 DelayedCopyableHandler sourceHandler = (DelayedCopyableHandler) handler;
159 return getDelayedCopy(sourceHandler.copy == null
160 ? sourceHandler.original : sourceHandler.copy);
161 }
162 }
163
164 return (Copyable) Proxy.newProxyInstance(sourceClass.getClassLoader(),
165 getMetadata(sourceClass).interfaces, new DelayedCopyableHandler(source));
166 }
167
168
169
170
171
172
173 private static class ClassMetadata {
174
175
176
177
178 private final Class<?>[] interfaces;
179
180
181
182
183
184
185 private ClassMetadata(Class<?> targetClass) {
186 List<Class<?>> interfaceList = new ArrayList<Class<?>>();
187
188 Class<?> currentClass = targetClass;
189 while (currentClass != Object.class && currentClass != null) {
190 for (Class<?> ifc : currentClass.getInterfaces()) {
191 if (!interfaceList.contains(ifc)) {
192 interfaceList.add(ifc);
193 }
194 }
195 currentClass = currentClass.getSuperclass();
196 }
197
198
199 interfaces = interfaceList.toArray(new Class<?>[interfaceList.size()]);
200 }
201 }
202
203
204
205
206 private static final Map<Class<?>, ClassMetadata> CLASS_META_CACHE =
207 Collections.synchronizedMap(new WeakHashMap<Class<?>, ClassMetadata>());
208
209
210
211
212
213
214 private static final ClassMetadata getMetadata(Class<?> targetClass) {
215 ClassMetadata metadata = CLASS_META_CACHE.get(targetClass);
216
217 if (metadata == null) {
218 CLASS_META_CACHE.put(targetClass, metadata = new ClassMetadata(targetClass));
219 }
220
221 return metadata;
222 }
223
224 }