• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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")
Proxy()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      */
Proxy(InvocationHandler h)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      */
getProxyClass(ClassLoader loader, Class<?>... interfaces)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      */
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)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      */
isProxyClass(Class<?> cl)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      */
getInvocationHandler(Object proxy)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);
generateProxy(String name, Class[] interfaces, ClassLoader loader)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      */
constructorPrototype(InvocationHandler h)298     native private static void constructorPrototype(InvocationHandler h);
299     // END android-changed
300 
301 }
302