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">"duck typing"</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 }