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 }