• 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 /**
27  * {@code Proxy} defines methods for creating dynamic proxy classes and instances.
28  * A proxy class implements a declared set of interfaces and delegates method
29  * invocations to an {@code InvocationHandler}.
30  *
31  * @see InvocationHandler
32  * @since 1.3
33  */
34 public class Proxy implements Serializable {
35 
36     private static final long serialVersionUID = -2222568056686623797L;
37 
38     // maps class loaders to created classes by interface names
39     private static final Map<ClassLoader, Map<String, WeakReference<Class<?>>>> loaderCache = new WeakHashMap<ClassLoader, Map<String, WeakReference<Class<?>>>>();
40 
41     // to find previously created types
42     private static final Map<Class<?>, String> proxyCache = new WeakHashMap<Class<?>, String>();
43 
44     private static int NextClassNameIndex = 0;
45 
46     /**
47      * The invocation handler on which the method calls are dispatched.
48      */
49     protected InvocationHandler h;
50 
51     @SuppressWarnings("unused")
Proxy()52     private Proxy() {
53     }
54 
55     /**
56      * Constructs a new {@code Proxy} instance with the specified invocation
57      * handler.
58      *
59      * @param h
60      *            the invocation handler for the newly created proxy
61      */
Proxy(InvocationHandler h)62     protected Proxy(InvocationHandler h) {
63         this.h = h;
64     }
65 
66     /**
67      * Returns the dynamically built {@code Class} for the specified interfaces.
68      * Creates a new {@code Class} when necessary. The order of the interfaces
69      * is relevant. Invocations of this method with the same interfaces but
70      * different order result in different generated classes. The interfaces
71      * must be visible from the supplied class loader; no duplicates are
72      * permitted. All non-public interfaces must be defined in the same package.
73      *
74      * @param loader
75      *            the class loader that will define the proxy class
76      * @param interfaces
77      *            an array of {@code Class} objects, each one identifying an
78      *            interface that will be implemented by the returned proxy
79      *            class
80      * @return a proxy class that implements all of the interfaces referred to
81      *         in the contents of {@code interfaces}
82      * @throws IllegalArgumentException
83      *                if any of the interface restrictions are violated
84      * @throws NullPointerException
85      *                if either {@code interfaces} or any of its elements are
86      *                {@code null}
87      */
getProxyClass(ClassLoader loader, Class<?>... interfaces)88     public static Class<?> getProxyClass(ClassLoader loader,
89             Class<?>... interfaces) throws IllegalArgumentException {
90         // check that interfaces are a valid array of visible interfaces
91         if (interfaces == null) {
92             throw new NullPointerException();
93         }
94         String commonPackageName = null;
95         for (int i = 0, length = interfaces.length; i < length; i++) {
96             Class<?> next = interfaces[i];
97             if (next == null) {
98                 throw new NullPointerException();
99             }
100             String name = next.getName();
101             if (!next.isInterface()) {
102                 throw new IllegalArgumentException(name + " is not an interface");
103             }
104             if (loader != next.getClassLoader()) {
105                 try {
106                     if (next != Class.forName(name, false, loader)) {
107                         throw new IllegalArgumentException(name +
108                                 " is not visible from class loader");
109                     }
110                 } catch (ClassNotFoundException ex) {
111                     throw new IllegalArgumentException(name + " is not visible from class loader");
112                 }
113             }
114             for (int j = i + 1; j < length; j++) {
115                 if (next == interfaces[j]) {
116                     throw new IllegalArgumentException(name + " appears more than once");
117                 }
118             }
119             if (!Modifier.isPublic(next.getModifiers())) {
120                 int last = name.lastIndexOf('.');
121                 String p = last == -1 ? "" : name.substring(0, last);
122                 if (commonPackageName == null) {
123                     commonPackageName = p;
124                 } else if (!commonPackageName.equals(p)) {
125                     throw new IllegalArgumentException("non-public interfaces must be " +
126                             "in the same package");
127                 }
128             }
129         }
130 
131         // search cache for matching proxy class using the class loader
132         synchronized (loaderCache) {
133             Map<String, WeakReference<Class<?>>> interfaceCache = loaderCache
134                     .get(loader);
135             if (interfaceCache == null) {
136                 loaderCache
137                         .put(
138                                 loader,
139                                 (interfaceCache = new HashMap<String, WeakReference<Class<?>>>()));
140             }
141 
142             String interfaceKey = "";
143             if (interfaces.length == 1) {
144                 interfaceKey = interfaces[0].getName();
145             } else {
146                 StringBuilder names = new StringBuilder();
147                 for (int i = 0, length = interfaces.length; i < length; i++) {
148                     names.append(interfaces[i].getName());
149                     names.append(' ');
150                 }
151                 interfaceKey = names.toString();
152             }
153 
154             Class<?> newClass;
155             WeakReference<Class<?>> ref = interfaceCache.get(interfaceKey);
156             if (ref == null) {
157                 String nextClassName = "$Proxy" + NextClassNameIndex++;
158                 if (commonPackageName != null && commonPackageName.length() > 0) {
159                     nextClassName = commonPackageName + "." + nextClassName;
160                 }
161                 if (loader == null) {
162                     loader = ClassLoader.getSystemClassLoader();
163                 }
164                 newClass = generateProxy(nextClassName.replace('.', '/'), interfaces, loader);
165                 // Need a weak reference to the class so it can
166                 // be unloaded if the class loader is discarded
167                 interfaceCache.put(interfaceKey, new WeakReference<Class<?>>(newClass));
168                 synchronized (proxyCache) {
169                     // the value is unused
170                     proxyCache.put(newClass, "");
171                 }
172             } else {
173                 newClass = ref.get();
174                 assert newClass != null : "\ninterfaceKey=\"" + interfaceKey + "\""
175                                         + "\nloaderCache=\"" + loaderCache + "\""
176                                         + "\nintfCache=\"" + interfaceCache + "\""
177                                         + "\nproxyCache=\"" + proxyCache + "\"";
178             }
179             return newClass;
180         }
181     }
182 
183     /**
184      * Returns an instance of the dynamically built class for the specified
185      * interfaces. Method invocations on the returned instance are forwarded to
186      * the specified invocation handler. The interfaces must be visible from the
187      * supplied class loader; no duplicates are permitted. All non-public
188      * interfaces must be defined in the same package.
189      *
190      * @param loader
191      *            the class loader that will define the proxy class
192      * @param interfaces
193      *            an array of {@code Class} objects, each one identifying an
194      *            interface that will be implemented by the returned proxy
195      *            object
196      * @param h
197      *            the invocation handler that handles the dispatched method
198      *            invocations
199      * @return a new proxy object that delegates to the handler {@code h}
200      * @throws IllegalArgumentException
201      *                if any of the interface restrictions are violated
202      * @throws NullPointerException
203      *                if the interfaces or any of its elements are null
204      */
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)205     public static Object newProxyInstance(ClassLoader loader,
206             Class<?>[] interfaces, InvocationHandler h)
207             throws IllegalArgumentException {
208         if (h == null) {
209             throw new NullPointerException();
210         }
211         try {
212             return getProxyClass(loader, interfaces).getConstructor(
213                     new Class<?>[] { InvocationHandler.class }).newInstance(
214                     new Object[] { h });
215         } catch (NoSuchMethodException ex) {
216             throw (InternalError) (new InternalError(ex.toString())
217                     .initCause(ex));
218         } catch (IllegalAccessException ex) {
219             throw (InternalError) (new InternalError(ex.toString())
220                     .initCause(ex));
221         } catch (InstantiationException ex) {
222             throw (InternalError) (new InternalError(ex.toString())
223                     .initCause(ex));
224         } catch (InvocationTargetException ex) {
225             Throwable target = ex.getTargetException();
226             throw (InternalError) (new InternalError(target.toString())
227                     .initCause(target));
228         }
229     }
230 
231     /**
232      * Indicates whether or not the specified class is a dynamically generated
233      * proxy class.
234      *
235      * @param cl
236      *            the class
237      * @return {@code true} if the class is a proxy class, {@code false}
238      *         otherwise
239      * @throws NullPointerException
240      *                if the class is {@code null}
241      */
isProxyClass(Class<?> cl)242     public static boolean isProxyClass(Class<?> cl) {
243         if (cl == null) {
244             throw new NullPointerException();
245         }
246         synchronized (proxyCache) {
247             return proxyCache.containsKey(cl);
248         }
249     }
250 
251     /**
252      * Returns the invocation handler of the specified proxy instance.
253      *
254      * @param proxy
255      *            the proxy instance
256      * @return the invocation handler of the specified proxy instance
257      * @throws IllegalArgumentException
258      *                if the supplied {@code proxy} is not a proxy object
259      */
getInvocationHandler(Object proxy)260     public static InvocationHandler getInvocationHandler(Object proxy)
261             throws IllegalArgumentException {
262 
263         if (isProxyClass(proxy.getClass())) {
264             return ((Proxy) proxy).h;
265         }
266 
267         throw new IllegalArgumentException("not a proxy instance");
268     }
269 
generateProxy(String name, Class[] interfaces, ClassLoader loader)270     native private static Class generateProxy(String name, Class[] interfaces,
271         ClassLoader loader);
272 
273     /*
274      * The VM clones this method's descriptor when generating a proxy class.
275      * There is no implementation.
276      */
constructorPrototype(InvocationHandler h)277     native private static void constructorPrototype(InvocationHandler h);
278 }
279