1 //---------------------------------------------------------------------- 2 // 3 // PerfectJPattern: "Design patterns are good but components are better!" 4 // AbstractSurrogate.java Copyright (c) 2009 Giovanni Azua Garcia 5 // bravegag@hotmail.com 6 // 7 // This program is free software; you can redistribute it and/or 8 // modify it under the terms of the GNU General Public License 9 // as published by the Free Software Foundation; either version 3 10 // of the License, or (at your option) any later version. 11 // 12 // This program is distributed in the hope that it will be useful, 13 // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 // GNU General Public License for more details. 16 // 17 // You should have received a copy of the GNU General Public License 18 // along with this program; if not, see <http://www.gnu.org/licenses/>. 19 // 20 //---------------------------------------------------------------------- 21 package org.perfectjpattern.core.structural; 22 23 import java.lang.reflect.*; 24 import java.util.*; 25 26 import org.apache.commons.lang.*; 27 import org.perfectjpattern.core.api.structural.*; 28 29 /** 30 * Concrete implementation of {@link ISurrogate} that groups common 31 * functionality for single instance surrogates. 32 * 33 * @param <C> <code>Component</code> or wrapper element type 34 * @param <U> <code>Underlying</code> wrapped element type 35 * 36 * @author <a href="mailto:bravegag@hotmail.com">Giovanni Azua</a> 37 * @version $Revision: 1.0 $Date: Apr 5, 2008 4:04:47 PM $ 38 */ 39 public abstract 40 class AbstractSurrogate<C, U> 41 implements ISurrogate<C> 42 { 43 //------------------------------------------------------------------------ 44 // public 45 //------------------------------------------------------------------------ 46 /** 47 * Creates an AbstractSurrogate<C> given a <code>Component</code> 48 * interface type and an instance of the underlying wrapped object. 49 * 50 * @param aSurrogateClass The desired <code>Surrogate</code> interface type 51 * @param anUnderlying <code>Underlying</code> instance to surrogate 52 * @throws IllegalArgumentException 'anInterface' must not be null 53 * @throws IllegalArgumentException 'anInterface' must be an interface type 54 * @throws IllegalArgumentException 'aComponent' must not be null 55 */ 56 @SuppressWarnings("unchecked") 57 public 58 AbstractSurrogate(Class<C> aSurrogateClass, U anUnderlying) 59 throws IllegalArgumentException 60 { 61 Validate.notNull(aSurrogateClass, "'anInterface' must not be null"); 62 Validate.isTrue(aSurrogateClass.isInterface(), 63 "'aSurrogateClass' must be an interface type"); 64 Validate.notNull(anUnderlying, "'anUnderlying' must not be null"); 65 66 theComponentClass = aSurrogateClass; 67 theUnderlying = anUnderlying; 68 69 final ClassLoader myClassLoader = aSurrogateClass.getClassLoader(); 70 theComponent = (C) Proxy.newProxyInstance(myClassLoader, new Class[] { 71 aSurrogateClass }, this); 72 } 73 74 //------------------------------------------------------------------------ 75 /** 76 * {@inheritDoc} 77 */ 78 public final C 79 getComponent() 80 { 81 return theComponent; 82 } 83 84 //------------------------------------------------------------------------ 85 public final Object 86 invoke(Object aProxy, Method aMethod, Object[] anArguments) 87 throws Throwable 88 { 89 Object myResult = null; 90 91 Method myMethod = null; 92 if (theLookup.containsKey(aMethod)) 93 { 94 myMethod = theLookup.get(aMethod); 95 } 96 else 97 { 98 try 99 { 100 // check whether this method is implemented by the 101 // surrogate i.e. this 102 myMethod = getClass().getMethod(aMethod.getName(), aMethod. 103 getParameterTypes()); 104 theLookup.put(aMethod, myMethod); 105 } 106 catch (NoSuchMethodException anException) 107 { 108 // pass it on to the underlying then 109 } 110 } 111 112 // if the method is decorated then pass control to the surrogate 113 // otherwise forward to the underlying to execute it 114 if (myMethod != null) 115 { 116 myResult = myMethod.invoke(this, anArguments); 117 } 118 else 119 { 120 // first make sure that the underlying implements such method 121 try 122 { 123 // invoke underlying 124 myResult = invokeUnderlying(aMethod, anArguments); 125 126 // cache the association 127 theLookup.put(aMethod, myMethod); 128 } 129 catch (NoSuchMethodException anException) 130 { 131 throw new NoSuchMethodError("Underlying component does not " + 132 "implement '" + aMethod.getName() + "'"); 133 } 134 } 135 136 return myResult; 137 } 138 139 //------------------------------------------------------------------------ 140 /** 141 * {@inheritDoc} 142 */ 143 @Override 144 public final boolean 145 equals(Object anObject) 146 { 147 boolean myResult = false; 148 149 // FindBugs successfully identifies this equals implementation as 150 // unusual, the reason is that a Surrogate impersonates the real 151 // instance so this equals implementation is an attempt to provide 152 // the same identity view of the Surrogate 153 if (anObject instanceof Proxy) 154 { 155 Proxy myProxy = (Proxy) anObject; 156 157 myResult = myProxy.equals(theUnderlying); 158 } 159 else 160 { 161 myResult = theUnderlying.equals(anObject); 162 } 163 164 return myResult; 165 } 166 167 //------------------------------------------------------------------------ 168 /** 169 * {@inheritDoc} 170 */ 171 @Override 172 public final int 173 hashCode() 174 { 175 return theUnderlying.hashCode(); 176 } 177 178 //------------------------------------------------------------------------ 179 // protected 180 //------------------------------------------------------------------------ 181 /** 182 * {@inheritDoc} 183 */ 184 protected final U 185 getUnderlying() 186 { 187 return theUnderlying; 188 } 189 190 //------------------------------------------------------------------------ 191 /** 192 * Returns the result of the Method invocation. This method has two 193 * main purposes: 194 * 195 * <ul> 196 * <li>Provides facility to invoke a method on the actual Underlying</li> 197 * <li>Concrete surrogate implementations that override this method have 198 * a single point to control access to the actual Underlying method 199 * invokation.</li> 200 * </ul> 201 * 202 * @param aMethod Method to invoke 203 * @param anArguments Array of input arguments for the Method to invoke 204 * @return result of the Method invocation 205 * @throws Throwable 206 */ 207 protected Object 208 invokeUnderlying(Method aMethod, Object[] anArguments) 209 throws Throwable 210 { 211 return aMethod.invoke(theUnderlying, anArguments); 212 } 213 214 //------------------------------------------------------------------------ 215 /** 216 * Returns the Component class 217 * 218 * @return the Component class 219 */ 220 protected final Class<C> 221 getComponentClass() 222 { 223 return theComponentClass; 224 } 225 226 //------------------------------------------------------------------------ 227 // members 228 //------------------------------------------------------------------------ 229 /** 230 * <code>Component</code> instance 231 */ 232 private final C theComponent; 233 234 //------------------------------------------------------------------------ 235 /** 236 * <code>Component</code> class type 237 */ 238 private final Class<C> theComponentClass; 239 240 /** 241 * Reference to the actual <code>Underlying</code> type wrapped by this 242 * surrogate instance. 243 */ 244 private final U theUnderlying; 245 246 /** 247 * <code>Map<Method, Method><code> that contains all methods 248 * implemented by the <code>Component</code> type 249 */ 250 private final Map<Method, Method> theLookup = new HashMap<Method, Method>(); 251 }