KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > proxy > compiler > Proxies


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software 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 GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.proxy.compiler;
23
24 import java.lang.reflect.Method JavaDoc;
25 import java.lang.reflect.Constructor JavaDoc;
26 import java.lang.reflect.InvocationTargetException JavaDoc;
27 import java.lang.reflect.Modifier JavaDoc;
28 import java.lang.reflect.Member JavaDoc;
29
30 import java.io.Serializable JavaDoc;
31
32 import java.util.ArrayList JavaDoc;
33 import java.util.Hashtable JavaDoc;
34
35 /**
36  * Routines for converting between strongly-typed interfaces and
37  * generic InvocationHandler objects.
38  *
39  * @version <tt>$Revision: 37459 $</tt>
40  * @author Unknown
41  * @author <a HREF="mailto:jason@planet57.com">Jason Dillon</a>
42  */

43 public final class Proxies
44 {
45    /**
46     * Disallow creation of Proxyies instances.
47     */

48    private Proxies()
49    {
50       super();
51    }
52    
53    /**
54     * Create a new target object <em>x</em> which is a proxy for
55     * the given InvocationHandler <tt>disp</tt>. The new object will be
56     * equivalent to <tt>disp</tt>, except that it will support normal Java
57     * method invocation, in place of the <tt>InvocationHandler.invoke</tt>
58     * protocol, for each method supported by the InvocationHandler.
59     *
60     * <p>
61     * The new object will implement each of the given target types.
62     * (The target types default to those declared by the InvocationHandler
63     * itself.) The new object will also implement the "administrative"
64     * interface <tt>Proxies.ProxyTarget</tt>.
65     *
66     * <p>
67     * For each "overrideable" (public, non-static, non-final)
68     * method <tt>T.m</tt> of <tt>T</tt>, calling <tt>x.m(...)</tt>
69     * will immediately cause a corresponding reflective call of the
70     * form <tt>disp.invoke(RM, new Object[]{ ... })</tt>, where <tt>RM</tt>
71     * is the reflective object for <tt>m</tt>.
72     *
73     * <p>
74     * The concrete class of this target object will be
75     * something mysterious and indefinite. Many callers will
76     * immediately cast the resulting proxy object to the target type
77     * of the InvocationHandler. For example:
78     * <code>
79     * MyInterface x1 = ...;
80     * InvocationHandler i = Proxies.newInvocationHandler(x1, MyInterface.class);
81     * MyInterface x2 = (MyInterface) ((Proxies.ProxyInvocationHandler)i).getTarget();
82     * // x1 == x2
83     * MyInterface x3 = (MyInterface) Proxies.newTarget(i);
84     * // x1 != x3, but calls to x3 are forwarded via i to x1
85     * </code>
86     */

87    public static ProxyTarget newTarget(ClassLoader JavaDoc parent,
88                                        InvocationHandler invocationHandler,
89                                        Class JavaDoc targetTypes[])
90       throws Exception JavaDoc
91    {
92       return Impl.getImpl(targetTypes).newTarget(invocationHandler, parent);
93    }
94    
95    /**
96     * A common interface shared by all objects created
97     * by <tt>Proxies.newTarget</tt>.
98     */

99    public interface ProxyTarget
100       extends Serializable JavaDoc
101    {
102       /**
103        * Recover the original InvocationHandler object around which this
104        * proxy is wrapped.
105        */

106       InvocationHandler getInvocationHandler();
107       
108       /**
109        * Recover the original target types for which this proxy was wrapped.
110        */

111       Class JavaDoc[] getTargetTypes();
112    }
113    
114    /**
115     * Create a new reflective InvocationHandler object
116     * <tt>InvocationHandler</tt> wrapped around the given target object, for
117     * the given target type(s).
118     *
119     * <p>
120     * The new object will be operationally equivalent to <tt>target</tt>,
121     * except that it will support a reflective method invocation sequence
122     * (<tt>InvocationHandler.invoke</tt>) instead of the normal Java method
123     * invocation mechanism.
124     *
125     * <p>
126     * The target type must be specified, since the complete implementation
127     * type of the target object is usually irrelevant to the application.
128     * The target object must match the specified target type.
129     * For example:
130     * <code>
131     * MyInterface x1 = ...;
132     * InvocationHandler i = Proxies.newInvocationHandler(x1, MyInterface.class);
133     * </code>
134     */

135    public static ProxyInvocationHandler newInvocationHandler(Object JavaDoc target,
136                                                              Class JavaDoc targetType)
137    {
138       return Impl.getImpl(targetType).newInvocationHandler(target);
139    }
140    
141    public static ProxyInvocationHandler newInvocationHandler(Object JavaDoc target,
142                                                              Class JavaDoc targetTypes[])
143    {
144       return Impl.getImpl(targetTypes).newInvocationHandler(target);
145    }
146    
147    /**
148     * A common interface shared by all objects created
149     * by <tt>Proxies.newInvocationHandler</tt>.
150     */

151    public interface ProxyInvocationHandler
152       extends InvocationHandler, Serializable JavaDoc
153    {
154       /**
155        * Recover the original target object around which this
156        * InvocationHandler proxy is wrapped.
157        */

158       Object JavaDoc getTarget();
159    }
160    
161    /**
162     * Utility built on top of <tt>newTarget</tt> to find
163     * or create a proxy for the given InvocationHandler.
164     * It is the inverse of <tt>getInvocationHandler</tt>.
165     *
166     * <p>
167     * If the InvocationHandler implements <tt>ProxyInvocationHandler</tt>,
168     * it is a proxy for some original target object; extract and return
169     * that object. Otherwise, just call <tt>newTarget</tt>.
170     */

171    public static Object JavaDoc getTarget(InvocationHandler invocationHandler)
172    {
173       if (invocationHandler instanceof ProxyInvocationHandler)
174       {
175          Object JavaDoc target = ((ProxyInvocationHandler)invocationHandler).getTarget();
176          if (target != null)
177          {
178             return target;
179          }
180          // and fall through...
181
}
182       
183       return null;
184    }
185    
186    /**
187     * Utility built on top of <tt>newInvocationHandler</tt> to find
188     * or create a proxy for the given target object.
189     * It is the inverse of <tt>getTarget</tt>.
190     *
191     * <p>
192     * If the target implements <tt>ProxyTarget</tt>, it is a proxy
193     * for some original InvocationHandler; extract and return that
194     * InvocationHandler. Otherwise, just call <tt>newInvocationHandler</tt>.
195     *
196     * @see #newInvocationHandler
197     */

198    public static InvocationHandler getInvocationHandler(Object JavaDoc target,
199                                                         Class JavaDoc targetTypes[])
200    {
201       if (target instanceof ProxyTarget)
202       {
203          ProxyTarget tproxy = (ProxyTarget)target;
204          InvocationHandler invocationHandler = tproxy.getInvocationHandler();
205          if (targetTypes == null ||
206          Impl.sameTypes(tproxy.getTargetTypes(), targetTypes))
207          {
208             return invocationHandler;
209          }
210          // and fall through...
211
}
212       return newInvocationHandler(target, targetTypes);
213    }
214    
215    public static InvocationHandler getInvocationHandler(Object JavaDoc target,
216                                                         Class JavaDoc targetType)
217    {
218       // (should this be optimized?)
219
if (targetType == null)
220       {
221          return getInvocationHandler(target, (Class JavaDoc[])null);
222       }
223
224       return getInvocationHandler(target, new Class JavaDoc[] { targetType });
225    }
226    
227    /**
228     * Utility which reports the set of valid methods for a target type.
229     * It is exactly the set of <tt>public</tt>, <tt>abstract</tt> methods
230     * returned by <tt>targetType.getMethods()</tt>, which are neither
231     * <tt>static</tt> nor <tt>final</tt>.
232     * <p>
233     * Also, if the targetType is not a suitable type, an empty array
234     * will be returned. The target type must not contain <tt>protected</tt>
235     * <tt>abstract</tt> methods, must have a nullary constructor,
236     * and must not be something silly like
237     * an array or primitive type, or a <tt>final</tt> class.
238     */

239    public static Method JavaDoc[] getMethods(Class JavaDoc targetType)
240    {
241       return Impl.getImpl(targetType).copyMethods();
242    }
243    
244    public static Method JavaDoc[] getMethods(Class JavaDoc targetTypes[])
245    {
246       return Impl.getImpl(targetTypes).copyMethods();
247    }
248
249    public static void forgetProxyForClass(Class JavaDoc clazz)
250    {
251       Impl.forgetProxyForClass(clazz);
252    }
253    
254    /**
255     * ???
256     */

257    static class Impl
258       implements Serializable JavaDoc
259    {
260       static Hashtable JavaDoc impls = new Hashtable JavaDoc();
261       
262       /** the types that this impl processes */
263       Class JavaDoc targetTypes[];
264       
265       Method JavaDoc methods[];
266       
267       /** hashtable link to Impls sharing a target type */
268       Impl more;
269       
270       Class JavaDoc superclass = Object JavaDoc.class;
271       
272       /** used in print names of proxies */
273       String JavaDoc proxyString;
274       
275       Constructor JavaDoc proxyConstructor;
276       
277       Impl(Class JavaDoc targetTypes[])
278       {
279          this.targetTypes = targetTypes;
280          
281          Method JavaDoc methodLists[][] = new Method JavaDoc[targetTypes.length][];
282          for (int i = 0; i < targetTypes.length; i++)
283          {
284             methodLists[i] = checkTargetType(targetTypes[i]);
285          }
286
287          checkSuperclass();
288          this.methods = combineMethodLists(methodLists);
289       }
290       
291       static synchronized Impl getImpl(Class JavaDoc targetType)
292       {
293          Impl impl = (Impl) impls.get(targetType);
294          if (impl == null)
295          {
296             impl = new Impl(new Class JavaDoc[] { targetType });
297             impls.put(targetType, impl);
298          }
299          return impl;
300       }
301       
302       static synchronized Impl getImpl(Class JavaDoc targetTypes[])
303       {
304          int n = targetTypes.length;
305          if (n == 1)
306          {
307             return getImpl(targetTypes[0]);
308          }
309          // note that the desired Impl could be in any one of n places
310
// this requires extra searching, which is not a big deal
311
for (int i = 0; i < n; ++i)
312          {
313             for (Impl impl = (Impl) impls.get(targetTypes[i]); impl != null; impl = impl.more)
314             {
315                if (sameTypes(targetTypes, impl.targetTypes))
316                   return impl;
317             }
318          }
319          
320          // now link it into the table
321
targetTypes = copyAndUniquify(targetTypes);
322          Impl impl1 = getImpl(new Class JavaDoc[] { targetTypes[0] });
323          Impl impl = new Impl(targetTypes);
324          impl.more = impl1.more;
325          impl1.more = impl;
326          return impl;
327       }
328       
329       /**
330        * The <code>forgetProxyForClass</code> method removes the impl from the
331        * class-impl map. This releases the UnifiedClassloader used to load the
332        * class we are constructing the proxy for.
333        *
334        * This may not work if the original class[] contained many classes, but
335        * seems OK with one class + Serializable, which is what is used by the cmp2
336        * engine. At present the cmp2 engine is the only caller of this method
337        * (through Proxy).
338        *
339        * @param clazz a <code>Class</code> value
340        */

341       static synchronized void forgetProxyForClass(Class JavaDoc clazz)
342       {
343          impls.remove(clazz);
344       }
345
346
347       // do the arrays have the same elements?
348
// (duplication and reordering are ignored)
349
static boolean sameTypes(Class JavaDoc tt1[], Class JavaDoc tt2[])
350       {
351          if (tt1.length == 1 && tt2.length == 1)
352          {
353             return tt1[0] == tt2[0];
354          }
355          
356          int totalSeen2 = 0;
357          each_type:
358             for (int i = tt1.length; --i >= 0; )
359             {
360                Class JavaDoc c = tt1[i];
361                for (int j = i; --j >= 0; )
362                {
363                   if (c == tt1[j])
364                   {
365                      continue each_type;
366                   }
367                }
368                // now c is a uniquified element of tt1
369
// count its occurrences in tt2
370
int seen2 = 0;
371                for (int j = tt2.length; --j >= 0; )
372                {
373                   if (c == tt2[j])
374                   {
375                      ++seen2;
376                   }
377                }
378
379                if (seen2 == 0)
380                {
381                   // c does not occur in tt2
382
return false;
383                }
384                totalSeen2 += seen2;
385             }
386             // now, each element of tt2 must have been visited
387
return totalSeen2 == tt2.length;
388       }
389       
390       static Class JavaDoc[] copyAndUniquify(Class JavaDoc targetTypes[])
391       {
392          int n = targetTypes.length;
393          Class JavaDoc tt[] = new Class JavaDoc[n];
394          int k = 0;
395          each_type:
396             for (int i = 0; i < n; i++)
397             {
398                Class JavaDoc c = targetTypes[i];
399                for (int j = i; --j >= 0; )
400                {
401                   if (c == targetTypes[j])
402                   {
403                      continue each_type;
404                   }
405                }
406                tt[k++] = c;
407             }
408             if (k < n)
409             {
410                // oops; caller passed in duplicate
411
Class JavaDoc tt0[] = new Class JavaDoc[k];
412                for (int i = 0; i < k; i++)
413                {
414                   tt0[i] = tt[i];
415                }
416                tt = tt0;
417             }
418             return tt;
419       }
420       
421       // make sure a give target type is acceptable
422
// return a list of eligible methods (may also include nulls)
423
Method JavaDoc[] checkTargetType(Class JavaDoc targetType)
424       {
425          if (targetType.isArray())
426          {
427             throw new IllegalArgumentException JavaDoc
428                ("cannot subclass an array type: " + targetType.getName());
429          }
430
431          if (targetType.isPrimitive())
432          {
433             throw new IllegalArgumentException JavaDoc
434                ("cannot subclass a primitive type: " + targetType);
435          }
436
437          int tmod = targetType.getModifiers();
438          if (Modifier.isFinal(tmod))
439          {
440             throw new IllegalArgumentException JavaDoc
441                ("cannot subclass a final type: " + targetType);
442          }
443
444          if (!Modifier.isPublic(tmod))
445          {
446             throw new IllegalArgumentException JavaDoc
447                ("cannot subclass a non-public type: " + targetType);
448          }
449          
450          // Make sure the subclass will not need a "super" statement.
451
if (!targetType.isInterface())
452          {
453             if (!targetType.isAssignableFrom(superclass))
454             {
455                if (superclass.isAssignableFrom(targetType))
456                {
457                   superclass = targetType;
458                }
459                else {
460                   throw new IllegalArgumentException JavaDoc
461                      ("inconsistent superclass: " + targetType);
462                }
463             }
464          }
465          
466          // Decide what overrideable methods this type supports.
467
Method JavaDoc methodList[] = targetType.getMethods();
468          int nm = 0;
469          for (int i = 0; i < methodList.length; i++)
470          {
471             Method JavaDoc m = methodList[i];
472             if (eligibleForInvocationHandler(m))
473             {
474                methodList[nm++] = m; // (reuse the method array)
475
}
476          }
477
478          while (nm < methodList.length)
479          {
480             methodList[nm++] = null; // (pad the reused method array)
481
}
482          
483          return methodList;
484       }
485       
486       void checkSuperclass()
487       {
488          Constructor JavaDoc constructors[] = superclass.getConstructors();
489          for (int i = 0; i < constructors.length; i++)
490          {
491             Constructor JavaDoc c = constructors[i];
492             int mod = c.getModifiers();
493             if (Modifier.isPublic(mod)
494                 && c.getParameterTypes().length == 0)
495             {
496                return; // OK
497
}
498          }
499
500          throw new IllegalArgumentException JavaDoc
501             ("cannot subclass without nullary constructor: "
502              +superclass.getName());
503       }
504       
505       /**
506        * Tell if a given method will be passed by a proxy to its
507        * InvocationHandler
508        */

509       static boolean eligibleForInvocationHandler(Method JavaDoc m)
510       {
511          int mod = m.getModifiers();
512
513          if (Modifier.isStatic(mod) || Modifier.isFinal(mod))
514          {
515             // can't override these
516
return false;
517          }
518
519          if (!Modifier.isAbstract(mod))
520          {
521             // do not support methods with "super"
522
return false;
523          }
524
525          return true;
526       }
527       
528       /**
529        * Are the 2 methods equal in terms of conflicting with each other.
530        * i.e. String toString() and Map toString() are equal since only one
531        * toString() can be defined in a class.
532        * Also fixes problems with complex inheritance graphs and j2se1.4
533        */

534       static boolean areEqual(Method JavaDoc m1, Method JavaDoc m2)
535       {
536          // Check method names.
537
if( ! m1.getName().equals(m2.getName()) )
538             return false;
539          
540          // Check parameters
541
Class JavaDoc a1[] = m1.getParameterTypes();
542          Class JavaDoc a2[] = m2.getParameterTypes();
543          if( a1.length != a2.length )
544             return false;
545          for( int i=0; i < a1.length; i++)
546             if( !a1[i].equals(a2[i]) )
547                return false;
548          
549          return true;
550       }
551
552       /**
553        * Combine the given list of method[]'s into one method[],
554        * removing any methods duplicates.
555        */

556       static Method JavaDoc[] combineMethodLists(Method JavaDoc methodLists[][])
557       {
558          int nm = 0;
559          for (int i = 0; i < methodLists.length; i++)
560          {
561             nm += methodLists[i].length;
562          }
563          Method JavaDoc methods[] = new Method JavaDoc[nm];
564
565          // Merge the methods into a single array.
566
nm=0;
567          for (int i = 0; i < methodLists.length; i++)
568             for (int j = 0; j < methodLists[i].length; j++)
569                methods[nm++]=methodLists[i][j];
570          
571          // Remove duplicate methods. (set them to null)
572
for( int i=0; i < methods.length; i++ )
573          {
574             if( methods[i] == null )
575                continue;
576             for( int j=i+1; j < methods.length; j++ )
577             {
578                if( methods[j] == null )
579                   continue;
580                if( areEqual(methods[i], methods[j]) )
581                {
582                   methods[j]=null;
583                   nm--;
584                }
585             }
586          }
587          
588          // shorten and copy the array
589
ArrayList JavaDoc tmp = new ArrayList JavaDoc();
590          for (int i = 0; i < methods.length; i++)
591          {
592             if( methods[i] != null )
593                tmp.add(methods[i]);
594          }
595          Method JavaDoc methodsCopy[] = new Method JavaDoc[tmp.size()];
596          tmp.toArray(methodsCopy);
597          return methodsCopy;
598       }
599       
600       Method JavaDoc[] copyMethods()
601       {
602          return (Method JavaDoc[])methods.clone();
603       }
604
605       Class JavaDoc[] copyTargetTypes()
606       {
607          return (Class JavaDoc[])targetTypes.clone();
608       }
609       
610       ProxyTarget newTarget(InvocationHandler invocationHandler,
611                             ClassLoader JavaDoc parent)
612          throws Exception JavaDoc
613       {
614          if (proxyConstructor == null)
615          {
616             // make the proxy constructor
617
ProxyCompiler pc = new ProxyCompiler(parent,
618                                                  superclass,
619                                                  targetTypes,
620                                                  methods);
621
622             Class JavaDoc type[] = { InvocationHandler.class };
623             proxyConstructor = pc.getProxyType().getConstructor(type);
624          }
625
626          Object JavaDoc args[] = { invocationHandler };
627          return (ProxyTarget)proxyConstructor.newInstance(args);
628       }
629       
630       ProxyInvocationHandler newInvocationHandler(final Object JavaDoc target)
631       {
632          if (proxyString == null)
633          {
634             String JavaDoc s = "InvocationHandler@" + targetTypes[0].getName();
635             for (int i = 1; i < targetTypes.length; i++)
636             {
637                s += "," + targetTypes[i].getName();
638             }
639             proxyString = s;
640          }
641
642          return new ProxyInvocationHandler()
643          {
644             // (ISSUE: Should this be made subclassable?)
645
public Object JavaDoc getTarget()
646             {
647                return target;
648             }
649             
650             public Class JavaDoc[] getTargetTypes()
651             {
652                return copyTargetTypes();
653             }
654             
655             public String JavaDoc toString()
656             {
657                return proxyString + "[" + target + "]";
658             }
659             
660             public Object JavaDoc invoke(Object JavaDoc dummy,
661                                  Method JavaDoc method,
662                                  Object JavaDoc values[])
663                throws Throwable JavaDoc
664             {
665                return Impl.this.invoke(target, method, values);
666             }
667          };
668       }
669       
670       /**
671        * The heart of a ProxyInvocationHandler.
672        */

673       Object JavaDoc invoke(Object JavaDoc target, Member JavaDoc method, Object JavaDoc values[])
674          throws Throwable JavaDoc
675       {
676          // Note:
677
//
678
// We will not invoke the method unless we are expecting it.
679
// Thus, we cannot blindly call Method.invoke, but must first
680
// check our list of allowed methods.
681

682          try
683          {
684             Method JavaDoc methods[] = this.methods; // cache
685

686             // use fast pointer equality (it usually succeeds)
687
for (int i = methods.length; --i >= 0; )
688             {
689                if (methods[i] == method)
690                {
691                   return methods[i].invoke(target, values);
692                }
693             }
694             
695             // special case: allow a null method to select the unique one
696
if (method == null)
697             {
698                if (methods.length == 1)
699                {
700                   return methods[0].invoke(target, values);
701                }
702                throw new IllegalArgumentException JavaDoc("non-unique method");
703             }
704             
705             // try the slower form of equality
706
for (int i = methods.length; --i >= 0; )
707             {
708                if (methods[i].equals(method))
709                {
710                   return methods[i].invoke(target, values);
711                }
712             }
713             
714          }
715          catch (InvocationTargetException JavaDoc e)
716          {
717             throw e.getTargetException();
718          }
719          
720          throw new IllegalArgumentException JavaDoc("method unexpected "+method);
721       }
722    }
723 }
724
Popular Tags