1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.beanmerger;
35
36 import java.lang.reflect.InvocationTargetException;
37 import java.lang.reflect.Method;
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Set;
43
44 import net.sf.cglib.proxy.Enhancer;
45
46 import org.apache.commons.beanutils.MethodUtils;
47 import org.apache.commons.proxy.Invoker;
48 import org.apache.commons.proxy.factory.cglib.CglibProxyFactory;
49
50
51
52
53
54
55
56 public class ProxyBasedBeanMerger extends BeanMergerBase {
57
58 @Override
59 protected Object mergeBean(List sources) {
60 Set<Class> types = new HashSet<Class>();
61 Class< ? > mostSpecificAssignableClass = sources.get(0).getClass();
62 while (Enhancer.isEnhanced(mostSpecificAssignableClass)) {
63 mostSpecificAssignableClass = mostSpecificAssignableClass.getSuperclass();
64 }
65
66 for (Object source : sources) {
67
68
69 Class clazz = source.getClass();
70 while (Enhancer.isEnhanced(clazz)) {
71 clazz = clazz.getSuperclass();
72 }
73
74 Collections.addAll(types, clazz.getInterfaces());
75
76 if (mostSpecificAssignableClass.isAssignableFrom(clazz)) {
77 mostSpecificAssignableClass = clazz;
78 }
79 }
80 types.add(mostSpecificAssignableClass);
81 try {
82 return new CglibProxyFactory().createInvokerProxy(newInvoker(sources), types.toArray(new Class< ? >[types.size()]));
83 } catch (RuntimeException e) {
84 log.error("Failed to create proxy for sources {} with types {}", sources, types);
85 throw e;
86 }
87 }
88
89 protected MergeInvoker newInvoker(List sources) {
90 return new MergeInvoker(this, sources);
91 }
92
93
94
95
96
97
98 protected static final class MergeInvoker implements Invoker {
99
100 private final List sources;
101
102 private final BeanMerger merger;
103
104 private MergeInvoker(BeanMerger merger, List sources) {
105 this.merger = merger;
106 this.sources = sources;
107 }
108
109 @Override
110 public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
111
112 if (arguments.length > 0) {
113
114 for (Object source : sources) {
115 if(method.getDeclaringClass().isInstance(source)){
116 return method.invoke(source, arguments);
117 }
118 }
119 throw new IllegalStateException("Can't call method " + method.getName() + " on any of the sources.");
120 }
121
122 List values = new ArrayList();
123 for (Object obj : sources) {
124 values.add(evaluate(obj, method, arguments));
125 }
126
127 return merger.merge(values);
128 }
129
130 private Object evaluate(Object obj, Method method, Object[] arguments) throws IllegalAccessException, InvocationTargetException {
131
132
133 if (method.getDeclaringClass().isAssignableFrom(obj.getClass())) {
134 return method.invoke(obj, arguments);
135 }
136
137
138 try {
139 return MethodUtils.invokeMethod(obj, method.getName(), arguments);
140 } catch (NoSuchMethodException e) {
141 return null;
142 }
143 }
144 }
145
146 }