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 18package java.lang.reflect; 19 20import java.io.Serializable; 21import java.lang.ref.WeakReference; 22import java.util.HashMap; 23import java.util.Map; 24import 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 */ 34public 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") 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 */ 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 */ 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("interfaces == null"); 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("interfaces[" + i + "] == null"); 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 */ 205 public static Object newProxyInstance(ClassLoader loader, 206 Class<?>[] interfaces, InvocationHandler h) 207 throws IllegalArgumentException { 208 if (h == null) { 209 throw new NullPointerException("h == null"); 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 */ 242 public static boolean isProxyClass(Class<?> cl) { 243 if (cl == null) { 244 throw new NullPointerException("cl == null"); 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 */ 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 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 */ 277 native private static void constructorPrototype(InvocationHandler h); 278} 279