Home | History | Annotate | Download | only in reflect
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.lang.reflect;
     19 
     20 import java.io.Serializable;
     21 import java.lang.ref.WeakReference;
     22 import java.util.HashMap;
     23 import java.util.Map;
     24 import java.util.WeakHashMap;
     25 
     26 // BEGIN android-removed
     27 // import org.apache.harmony.luni.internal.reflect.ProxyClassFile;
     28 // END android-removed
     29 
     30 import org.apache.harmony.luni.util.Msg;
     31 
     32 /**
     33  * {@code Proxy} defines methods for creating dynamic proxy classes and instances.
     34  * A proxy class implements a declared set of interfaces and delegates method
     35  * invocations to an {@code InvocationHandler}.
     36  *
     37  * @see InvocationHandler
     38  * @since 1.3
     39  */
     40 public class Proxy implements Serializable {
     41 
     42     private static final long serialVersionUID = -2222568056686623797L;
     43 
     44     // maps class loaders to created classes by interface names
     45     private static final Map<ClassLoader, Map<String, WeakReference<Class<?>>>> loaderCache = new WeakHashMap<ClassLoader, Map<String, WeakReference<Class<?>>>>();
     46 
     47     // to find previously created types
     48     private static final Map<Class<?>, String> proxyCache = new WeakHashMap<Class<?>, String>();
     49 
     50     private static int NextClassNameIndex = 0;
     51 
     52     /**
     53      * The invocation handler on which the method calls are dispatched.
     54      */
     55     protected InvocationHandler h;
     56 
     57     @SuppressWarnings("unused")
     58     private Proxy() {
     59     }
     60 
     61     /**
     62      * Constructs a new {@code Proxy} instance with the specified invocation
     63      * handler.
     64      *
     65      * @param h
     66      *            the invocation handler for the newly created proxy
     67      */
     68     protected Proxy(InvocationHandler h) {
     69         this.h = h;
     70     }
     71 
     72     /**
     73      * Returns the dynamically built {@code Class} for the specified interfaces.
     74      * Creates a new {@code Class} when necessary. The order of the interfaces
     75      * is relevant. Invocations of this method with the same interfaces but
     76      * different order result in different generated classes. The interfaces
     77      * must be visible from the supplied class loader; no duplicates are
     78      * permitted. All non-public interfaces must be defined in the same package.
     79      *
     80      * @param loader
     81      *            the class loader that will define the proxy class
     82      * @param interfaces
     83      *            an array of {@code Class} objects, each one identifying an
     84      *            interface that will be implemented by the returned proxy
     85      *            class
     86      * @return a proxy class that implements all of the interfaces referred to
     87      *         in the contents of {@code interfaces}
     88      * @throws IllegalArgumentException
     89      *                if any of the interface restrictions are violated
     90      * @throws NullPointerException
     91      *                if either {@code interfaces} or any of its elements are
     92      *                {@code null}
     93      */
     94     public static Class<?> getProxyClass(ClassLoader loader,
     95             Class<?>... interfaces) throws IllegalArgumentException {
     96         // BEGIN android-note
     97         // Changed parameter to be closer to the RI
     98         // END android-note
     99         // check that interfaces are a valid array of visible interfaces
    100         if (interfaces == null) {
    101             throw new NullPointerException();
    102         }
    103         String commonPackageName = null;
    104         for (int i = 0, length = interfaces.length; i < length; i++) {
    105             Class<?> next = interfaces[i];
    106             if (next == null) {
    107                 throw new NullPointerException();
    108             }
    109             String name = next.getName();
    110             if (!next.isInterface()) {
    111                 throw new IllegalArgumentException(Msg.getString("K00ed", name)); //$NON-NLS-1$
    112             }
    113             if (loader != next.getClassLoader()) {
    114                 try {
    115                     if (next != Class.forName(name, false, loader)) {
    116                         throw new IllegalArgumentException(Msg.getString(
    117                                 "K00ee", name)); //$NON-NLS-1$
    118                     }
    119                 } catch (ClassNotFoundException ex) {
    120                     throw new IllegalArgumentException(Msg.getString("K00ee", //$NON-NLS-1$
    121                             name));
    122                 }
    123             }
    124             for (int j = i + 1; j < length; j++) {
    125                 if (next == interfaces[j]) {
    126                     throw new IllegalArgumentException(Msg.getString("K00ef", //$NON-NLS-1$
    127                             name));
    128                 }
    129             }
    130             if (!Modifier.isPublic(next.getModifiers())) {
    131                 int last = name.lastIndexOf('.');
    132                 String p = last == -1 ? "" : name.substring(0, last); //$NON-NLS-1$
    133                 if (commonPackageName == null) {
    134                     commonPackageName = p;
    135                 } else if (!commonPackageName.equals(p)) {
    136                     throw new IllegalArgumentException(Msg.getString("K00f0")); //$NON-NLS-1$
    137                 }
    138             }
    139         }
    140 
    141         // search cache for matching proxy class using the class loader
    142         synchronized (loaderCache) {
    143             Map<String, WeakReference<Class<?>>> interfaceCache = loaderCache
    144                     .get(loader);
    145             if (interfaceCache == null) {
    146                 loaderCache
    147                         .put(
    148                                 loader,
    149                                 (interfaceCache = new HashMap<String, WeakReference<Class<?>>>()));
    150             }
    151 
    152             String interfaceKey = ""; //$NON-NLS-1$
    153             if (interfaces.length == 1) {
    154                 interfaceKey = interfaces[0].getName();
    155             } else {
    156                 StringBuilder names = new StringBuilder();
    157                 for (int i = 0, length = interfaces.length; i < length; i++) {
    158                     names.append(interfaces[i].getName());
    159                     names.append(' ');
    160                 }
    161                 interfaceKey = names.toString();
    162             }
    163 
    164             Class<?> newClass;
    165             WeakReference<Class<?>> ref = interfaceCache.get(interfaceKey);
    166             if (ref == null) {
    167                 String nextClassName = "$Proxy" + NextClassNameIndex++; //$NON-NLS-1$
    168                 if (commonPackageName != null && commonPackageName.length() > 0) {
    169                     nextClassName = commonPackageName + "." + nextClassName; //$NON-NLS-1$
    170                 }
    171                 // BEGIN android-changed
    172                 // byte[] classFileBytes = ProxyClassFile.generateBytes(
    173                 //         nextClassName, interfaces);
    174                 // newClass = defineClassImpl(loader, nextClassName.replace('.',
    175                 //         '/'), classFileBytes);
    176                 if (loader == null) {
    177                     loader = ClassLoader.getSystemClassLoader();
    178                 }
    179                 newClass = generateProxy(nextClassName.replace('.', '/'),
    180                         interfaces, loader);
    181                 // END android-changed
    182                 // Need a weak reference to the class so it can
    183                 // be unloaded if the class loader is discarded
    184                 interfaceCache.put(interfaceKey, new WeakReference<Class<?>>(
    185                         newClass));
    186                 synchronized (proxyCache) {
    187                     // the value is unused
    188                     proxyCache.put(newClass, ""); //$NON-NLS-1$
    189                 }
    190             } else {
    191                 newClass = ref.get();
    192                 assert newClass != null : "\ninterfaceKey=\"" + interfaceKey + "\""
    193                                         + "\nloaderCache=\"" + loaderCache + "\""
    194                                         + "\nintfCache=\"" + interfaceCache + "\""
    195                                         + "\nproxyCache=\"" + proxyCache + "\"";
    196             }
    197             return newClass;
    198         }
    199     }
    200 
    201     /**
    202      * Returns an instance of the dynamically built class for the specified
    203      * interfaces. Method invocations on the returned instance are forwarded to
    204      * the specified invocation handler. The interfaces must be visible from the
    205      * supplied class loader; no duplicates are permitted. All non-public
    206      * interfaces must be defined in the same package.
    207      *
    208      * @param loader
    209      *            the class loader that will define the proxy class
    210      * @param interfaces
    211      *            an array of {@code Class} objects, each one identifying an
    212      *            interface that will be implemented by the returned proxy
    213      *            object
    214      * @param h
    215      *            the invocation handler that handles the dispatched method
    216      *            invocations
    217      * @return a new proxy object that delegates to the handler {@code h}
    218      * @throws IllegalArgumentException
    219      *                if any of the interface restrictions are violated
    220      * @throws NullPointerException
    221      *                if the interfaces or any of its elements are null
    222      */
    223     public static Object newProxyInstance(ClassLoader loader,
    224             Class<?>[] interfaces, InvocationHandler h)
    225             throws IllegalArgumentException {
    226         if (h == null) {
    227             throw new NullPointerException();
    228         }
    229         try {
    230             return getProxyClass(loader, interfaces).getConstructor(
    231                     new Class<?>[] { InvocationHandler.class }).newInstance(
    232                     new Object[] { h });
    233         } catch (NoSuchMethodException ex) {
    234             throw (InternalError) (new InternalError(ex.toString())
    235                     .initCause(ex));
    236         } catch (IllegalAccessException ex) {
    237             throw (InternalError) (new InternalError(ex.toString())
    238                     .initCause(ex));
    239         } catch (InstantiationException ex) {
    240             throw (InternalError) (new InternalError(ex.toString())
    241                     .initCause(ex));
    242         } catch (InvocationTargetException ex) {
    243             Throwable target = ex.getTargetException();
    244             throw (InternalError) (new InternalError(target.toString())
    245                     .initCause(target));
    246         }
    247     }
    248 
    249     /**
    250      * Indicates whether or not the specified class is a dynamically generated
    251      * proxy class.
    252      *
    253      * @param cl
    254      *            the class
    255      * @return {@code true} if the class is a proxy class, {@code false}
    256      *         otherwise
    257      * @throws NullPointerException
    258      *                if the class is {@code null}
    259      */
    260     public static boolean isProxyClass(Class<?> cl) {
    261         if (cl == null) {
    262             throw new NullPointerException();
    263         }
    264         synchronized (proxyCache) {
    265             return proxyCache.containsKey(cl);
    266         }
    267     }
    268 
    269     /**
    270      * Returns the invocation handler of the specified proxy instance.
    271      *
    272      * @param proxy
    273      *            the proxy instance
    274      * @return the invocation handler of the specified proxy instance
    275      * @throws IllegalArgumentException
    276      *                if the supplied {@code proxy} is not a proxy object
    277      */
    278     public static InvocationHandler getInvocationHandler(Object proxy)
    279             throws IllegalArgumentException {
    280 
    281         if (isProxyClass(proxy.getClass())) {
    282             return ((Proxy) proxy).h;
    283         }
    284 
    285         throw new IllegalArgumentException(Msg.getString("K00f1")); //$NON-NLS-1$
    286     }
    287 
    288     // BEGIN android-changed
    289     //private static native Class<?> defineClassImpl(ClassLoader classLoader,
    290     //        String className, byte[] classFileBytes);
    291     native private static Class generateProxy(String name, Class[] interfaces,
    292         ClassLoader loader);
    293 
    294     /*
    295      * The VM clones this method's descriptor when generating a proxy class.
    296      * There is no implementation.
    297      */
    298     native private static void constructorPrototype(InvocationHandler h);
    299     // END android-changed
    300 
    301 }
    302