View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.proxy2.invoker;
19  
20  import java.lang.reflect.Method;
21  
22  import org.apache.commons.proxy2.Invoker;
23  import org.apache.commons.proxy2.ObjectProvider;
24  
25  /**
26   * An invoker which supports <a href="http://en.wikipedia.org/wiki/Duck_typing">&quot;duck typing&quot;</a>, meaning
27   * that it finds a matching method on the object returned from the target provider and invokes it. This class is useful
28   * for adapting an existing class to an interface it does not implement.
29   * <p>
30   * <b>Example:</b>
31   * </p>
32   * <p>
33   * 
34   * <pre>
35   * public class LegacyDuck // Does not implement interface!
36   * {
37   *   public void quack()
38   *   {
39   *     // Quacking logic...
40   *   }
41   * }
42   * <p/>
43   * public interface Duck
44   * {
45   *   public void quack();
46   * }
47   * <p/>
48   * ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck()); // Always returns a "legacy" duck
49   * DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider);
50   * Duck duck = ( Duck )proxyFactory.createInvokerProxy( invoker, new Class[] { Duck.class } );
51   * </pre>
52   * 
53   * </p>
54   */
55  public class DuckTypingInvoker implements Invoker
56  {
57      /** Serialization version */
58      private static final long serialVersionUID = 1L;
59  
60      //******************************************************************************************************************
61      // Fields
62      //******************************************************************************************************************
63  
64      private final ObjectProvider<?> targetProvider;
65  
66      //******************************************************************************************************************
67      // Constructors
68      //******************************************************************************************************************
69  
70      /**
71       * Create a new DuckTypingInvoker instance.
72       * 
73       * @param targetProvider
74       */
75      public DuckTypingInvoker(final ObjectProvider<?> targetProvider)
76      {
77          this.targetProvider = targetProvider;
78      }
79  
80      //******************************************************************************************************************
81      // Invoker Implementation
82      //******************************************************************************************************************
83  
84      /**
85       * {@inheritDoc}
86       */
87      @Override
88      public Object invoke(final Object proxy, final Method method, final Object[] arguments) throws Throwable
89      {
90          final Object target = targetProvider.getObject();
91          final Class<?> targetClass = target.getClass();
92          try
93          {
94              final Method targetMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());
95              if (method.getReturnType().isAssignableFrom(targetMethod.getReturnType()))
96              {
97                  return targetMethod.invoke(target, arguments);
98              }
99              throw new UnsupportedOperationException("Target type " + targetClass.getName()
100                     + " method has incompatible return type.");
101         }
102         catch (NoSuchMethodException e)
103         {
104             throw new UnsupportedOperationException("Target type " + targetClass.getName()
105                     + " does not have a method matching " + method + ".", e);
106         }
107     }
108 }