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