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 018package org.apache.commons.proxy2.invoker; 019 020import java.lang.reflect.Method; 021 022import org.apache.commons.proxy2.Invoker; 023import org.apache.commons.proxy2.ObjectProvider; 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 useful 028 * for adapting an existing class to an interface it does not implement. 029 * <p> 030 * <b>Example:</b> 031 * </p> 032 * <p> 033 * 034 * <pre> 035 * public class LegacyDuck // Does not implement interface! 036 * { 037 * public void quack() 038 * { 039 * // Quacking logic... 040 * } 041 * } 042 * <p/> 043 * public interface Duck 044 * { 045 * public void quack(); 046 * } 047 * <p/> 048 * ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck()); // Always returns a "legacy" duck 049 * DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider); 050 * Duck duck = ( Duck )proxyFactory.createInvokerProxy( invoker, new Class[] { Duck.class } ); 051 * </pre> 052 * 053 * </p> 054 */ 055public class DuckTypingInvoker implements Invoker 056{ 057 /** Serialization version */ 058 private static final long serialVersionUID = 1L; 059 060 //****************************************************************************************************************** 061 // Fields 062 //****************************************************************************************************************** 063 064 private final ObjectProvider<?> targetProvider; 065 066 //****************************************************************************************************************** 067 // Constructors 068 //****************************************************************************************************************** 069 070 /** 071 * Create a new DuckTypingInvoker instance. 072 * 073 * @param targetProvider 074 */ 075 public DuckTypingInvoker(final ObjectProvider<?> targetProvider) 076 { 077 this.targetProvider = targetProvider; 078 } 079 080 //****************************************************************************************************************** 081 // Invoker Implementation 082 //****************************************************************************************************************** 083 084 /** 085 * {@inheritDoc} 086 */ 087 @Override 088 public Object invoke(final Object proxy, final Method method, final Object[] arguments) throws Throwable 089 { 090 final Object target = targetProvider.getObject(); 091 final Class<?> targetClass = target.getClass(); 092 try 093 { 094 final Method targetMethod = targetClass.getMethod(method.getName(), method.getParameterTypes()); 095 if (method.getReturnType().isAssignableFrom(targetMethod.getReturnType())) 096 { 097 return targetMethod.invoke(target, arguments); 098 } 099 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}