001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.proxy.invoker; 019 020 import org.apache.commons.proxy.Invoker; 021 import org.apache.commons.proxy.ObjectProvider; 022 023 import java.lang.reflect.Method; 024 025 /** 026 * An invoker which supports <a href="http://en.wikipedia.org/wiki/Duck_typing">"duck typing"</a>, meaning 027 * that it finds a matching method on the object returned from the target provider and invokes it. This class is 028 * useful for adapting an existing class to an interface it does not implement. 029 * <p> 030 * <b>Example:</b> 031 * </p> 032 * <p> 033 * <pre> 034 * public class LegacyDuck // Does not implement interface! 035 * { 036 * public void quack() 037 * { 038 * // Quacking logic... 039 * } 040 * } 041 * <p/> 042 * public interface Duck 043 * { 044 * public void quack(); 045 * } 046 * <p/> 047 * ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck()); // Always returns a "legacy" duck 048 * DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider); 049 * Duck duck = ( Duck )proxyFactory.createInvokerProxy( invoker, new Class[] { Duck.class } ); 050 * </pre> 051 * </p> 052 */ 053 public class DuckTypingInvoker implements Invoker 054 { 055 //---------------------------------------------------------------------------------------------------------------------- 056 // Fields 057 //---------------------------------------------------------------------------------------------------------------------- 058 059 private final ObjectProvider targetProvider; 060 061 //---------------------------------------------------------------------------------------------------------------------- 062 // Constructors 063 //---------------------------------------------------------------------------------------------------------------------- 064 065 public DuckTypingInvoker( final ObjectProvider targetProvider ) 066 { 067 this.targetProvider = targetProvider; 068 } 069 070 //---------------------------------------------------------------------------------------------------------------------- 071 // Interface Invoker 072 //---------------------------------------------------------------------------------------------------------------------- 073 074 public Object invoke( final Object proxy, final Method method, final Object[] arguments ) throws Throwable 075 { 076 final Object target = targetProvider.getObject(); 077 final Class targetClass = target.getClass(); 078 try 079 { 080 final Method targetMethod = targetClass.getMethod( method.getName(), method.getParameterTypes() ); 081 if ( method.getReturnType().isAssignableFrom( targetMethod.getReturnType() ) ) 082 { 083 return targetMethod.invoke( target, arguments ); 084 } 085 throw new UnsupportedOperationException( 086 "Target type " + targetClass.getName() + " method has incompatible return type." ); 087 } 088 catch ( NoSuchMethodException e ) 089 { 090 throw new UnsupportedOperationException( 091 "Target type " + targetClass.getName() + " does not have a method matching " + method + "." ); 092 } 093 } 094 }