View Javadoc

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&lt;C&gt; 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&lt;Method, Method&gt;<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 }