View Javadoc

1   /**
2    * This file Copyright (c) 2008-2011 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
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   * Proxy-based bean merger.
53   *
54   * @version $Id$
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              // Skip up the class hierarchy to avoid proxying cglib proxy classes
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       * Merge Invoker.
95       *
96       * @version $Id$
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             // only merge calls to property getters
112             if (arguments.length > 0) {
113                 // invoke the method on the first possibility
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             // direct method call
133             if (method.getDeclaringClass().isAssignableFrom(obj.getClass())) {
134                 return method.invoke(obj, arguments);
135             }
136 
137             // ducking
138             try {
139                 return MethodUtils.invokeMethod(obj, method.getName(), arguments);
140             } catch (NoSuchMethodException e) {
141                 return null;
142             }
143         }
144     }
145 
146 }