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.proxy.invoker; 19 20 import org.apache.commons.proxy.Invoker; 21 import org.apache.commons.proxy.ObjectProvider; 22 23 import java.lang.reflect.Method; 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 28 * useful for adapting an existing class to an interface it does not implement. 29 * <p> 30 * <b>Example:</b> 31 * </p> 32 * <p> 33 * <pre> 34 * public class LegacyDuck // Does not implement interface! 35 * { 36 * public void quack() 37 * { 38 * // Quacking logic... 39 * } 40 * } 41 * <p/> 42 * public interface Duck 43 * { 44 * public void quack(); 45 * } 46 * <p/> 47 * ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck()); // Always returns a "legacy" duck 48 * DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider); 49 * Duck duck = ( Duck )proxyFactory.createInvokerProxy( invoker, new Class[] { Duck.class } ); 50 * </pre> 51 * </p> 52 */ 53 public class DuckTypingInvoker implements Invoker 54 { 55 //---------------------------------------------------------------------------------------------------------------------- 56 // Fields 57 //---------------------------------------------------------------------------------------------------------------------- 58 59 private final ObjectProvider targetProvider; 60 61 //---------------------------------------------------------------------------------------------------------------------- 62 // Constructors 63 //---------------------------------------------------------------------------------------------------------------------- 64 65 public DuckTypingInvoker( final ObjectProvider targetProvider ) 66 { 67 this.targetProvider = targetProvider; 68 } 69 70 //---------------------------------------------------------------------------------------------------------------------- 71 // Interface Invoker 72 //---------------------------------------------------------------------------------------------------------------------- 73 74 public Object invoke( final Object proxy, final Method method, final Object[] arguments ) throws Throwable 75 { 76 final Object target = targetProvider.getObject(); 77 final Class targetClass = target.getClass(); 78 try 79 { 80 final Method targetMethod = targetClass.getMethod( method.getName(), method.getParameterTypes() ); 81 if ( method.getReturnType().isAssignableFrom( targetMethod.getReturnType() ) ) 82 { 83 return targetMethod.invoke( target, arguments ); 84 } 85 throw new UnsupportedOperationException( 86 "Target type " + targetClass.getName() + " method has incompatible return type." ); 87 } 88 catch ( NoSuchMethodException e ) 89 { 90 throw new UnsupportedOperationException( 91 "Target type " + targetClass.getName() + " does not have a method matching " + method + "." ); 92 } 93 } 94 }