001: /*
002: * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.reflect.misc;
027:
028: import java.security.AllPermission;
029: import java.security.AccessController;
030: import java.security.PermissionCollection;
031: import java.security.SecureClassLoader;
032: import java.security.PrivilegedExceptionAction;
033: import java.security.CodeSource;
034: import java.io.InputStream;
035: import java.io.BufferedInputStream;
036: import java.io.IOException;
037: import java.net.URL;
038: import java.net.URLConnection;
039: import java.net.HttpURLConnection;
040: import java.lang.reflect.Method;
041: import java.lang.reflect.InvocationTargetException;
042: import java.lang.reflect.AccessibleObject;
043: import java.lang.reflect.Modifier;
044: import java.util.Collection;
045: import java.util.HashMap;
046: import java.util.Map;
047: import sun.net.www.ParseUtil;
048: import sun.security.util.SecurityConstants;
049:
050: class Trampoline {
051: private static Object invoke(Method m, Object obj, Object[] params)
052: throws InvocationTargetException, IllegalAccessException {
053: return m.invoke(obj, params);
054: }
055: }
056:
057: /*
058: * Create a trampoline class.
059: */
060: public final class MethodUtil extends SecureClassLoader {
061: private static String MISC_PKG = "sun.reflect.misc.";
062: private static String TRAMPOLINE = MISC_PKG + "Trampoline";
063: private static Method bounce = getTrampoline();
064:
065: private MethodUtil() {
066: super ();
067: }
068:
069: public static Method getMethod(Class cls, String name, Class[] args)
070: throws NoSuchMethodException {
071: ReflectUtil.checkPackageAccess(cls);
072: return cls.getMethod(name, args);
073: }
074:
075: public static Method[] getMethods(Class cls) {
076: ReflectUtil.checkPackageAccess(cls);
077: return cls.getMethods();
078: }
079:
080: /*
081: * Discover the public methods on public classes
082: * and interfaces accessible to any caller by calling
083: * Class.getMethods() and walking towards Object until
084: * we're done.
085: */
086: public static Method[] getPublicMethods(Class cls) {
087: // compatibility for update release
088: if (System.getSecurityManager() == null) {
089: return cls.getMethods();
090: }
091: Map sigs = new HashMap();
092: while (cls != null) {
093: boolean done = getInternalPublicMethods(cls, sigs);
094: if (done) {
095: break;
096: }
097: getInterfaceMethods(cls, sigs);
098: cls = cls.getSuperclass();
099: }
100: Collection c = sigs.values();
101: return (Method[]) c.toArray(new Method[c.size()]);
102: }
103:
104: /*
105: * Process the immediate interfaces of this class or interface.
106: */
107: private static void getInterfaceMethods(Class cls, Map sigs) {
108: Class[] intfs = cls.getInterfaces();
109: for (int i = 0; i < intfs.length; i++) {
110: Class intf = intfs[i];
111: boolean done = getInternalPublicMethods(intf, sigs);
112: if (!done) {
113: getInterfaceMethods(intf, sigs);
114: }
115: }
116: }
117:
118: /*
119: *
120: * Process the methods in this class or interface
121: */
122: private static boolean getInternalPublicMethods(Class cls, Map sigs) {
123: Method[] methods = null;
124: try {
125: /*
126: * This class or interface is non-public so we
127: * can't use any of it's methods. Go back and
128: * try again with a superclass or superinterface.
129: */
130: if (!Modifier.isPublic(cls.getModifiers())) {
131: return false;
132: }
133: if (!ReflectUtil.isPackageAccessible(cls)) {
134: return false;
135: }
136:
137: methods = cls.getMethods();
138: } catch (SecurityException se) {
139: return false;
140: }
141:
142: /*
143: * Check for inherited methods with non-public
144: * declaring classes. They might override and hide
145: * methods from their superclasses or
146: * superinterfaces.
147: */
148: boolean done = true;
149: for (int i = 0; i < methods.length; i++) {
150: Class dc = methods[i].getDeclaringClass();
151: if (!Modifier.isPublic(dc.getModifiers())) {
152: done = false;
153: break;
154: }
155: }
156:
157: if (done) {
158: /*
159: * We're done. Spray all the methods into
160: * the list and then we're out of here.
161: */
162: for (int i = 0; i < methods.length; i++) {
163: addMethod(sigs, methods[i]);
164: }
165: } else {
166: /*
167: * Simulate cls.getDeclaredMethods() by
168: * stripping away inherited methods.
169: */
170: for (int i = 0; i < methods.length; i++) {
171: Class dc = methods[i].getDeclaringClass();
172: if (cls.equals(dc)) {
173: addMethod(sigs, methods[i]);
174: }
175: }
176: }
177: return done;
178: }
179:
180: private static void addMethod(Map sigs, Method method) {
181: Signature signature = new Signature(method);
182: if (!sigs.containsKey(signature)) {
183: sigs.put(signature, method);
184: } else if (!method.getDeclaringClass().isInterface()) {
185: /*
186: * Superclasses beat interfaces.
187: */
188: Method old = (Method) sigs.get(signature);
189: if (old.getDeclaringClass().isInterface()) {
190: sigs.put(signature, method);
191: }
192: }
193: }
194:
195: /**
196: * A class that represents the unique elements of a method that will be a
197: * key in the method cache.
198: */
199: private static class Signature {
200: private String methodName;
201: private Class[] argClasses;
202:
203: private volatile int hashCode = 0;
204:
205: Signature(Method m) {
206: this .methodName = m.getName();
207: this .argClasses = m.getParameterTypes();
208: }
209:
210: public boolean equals(Object o2) {
211: if (this == o2) {
212: return true;
213: }
214: Signature that = (Signature) o2;
215: if (!(methodName.equals(that.methodName))) {
216: return false;
217: }
218: if (argClasses.length != that.argClasses.length) {
219: return false;
220: }
221: for (int i = 0; i < argClasses.length; i++) {
222: if (!(argClasses[i] == that.argClasses[i])) {
223: return false;
224: }
225: }
226: return true;
227: }
228:
229: /**
230: * Hash code computed using algorithm suggested in
231: * Effective Java, Item 8.
232: */
233: public int hashCode() {
234: if (hashCode == 0) {
235: int result = 17;
236: result = 37 * result + methodName.hashCode();
237: if (argClasses != null) {
238: for (int i = 0; i < argClasses.length; i++) {
239: result = 37
240: * result
241: + ((argClasses[i] == null) ? 0
242: : argClasses[i].hashCode());
243: }
244: }
245: hashCode = result;
246: }
247: return hashCode;
248: }
249: }
250:
251: /*
252: * Bounce through the trampoline.
253: */
254: public static Object invoke(Method m, Object obj, Object[] params)
255: throws InvocationTargetException, IllegalAccessException {
256: if (m.getDeclaringClass().equals(AccessController.class)
257: || m.getDeclaringClass().equals(Method.class))
258: throw new InvocationTargetException(
259: new UnsupportedOperationException(
260: "invocation not supported"));
261: try {
262: return bounce.invoke(null, new Object[] { m, obj, params });
263: } catch (InvocationTargetException ie) {
264: Throwable t = ie.getCause();
265:
266: if (t instanceof InvocationTargetException) {
267: throw (InvocationTargetException) t;
268: } else if (t instanceof IllegalAccessException) {
269: throw (IllegalAccessException) t;
270: } else if (t instanceof RuntimeException) {
271: throw (RuntimeException) t;
272: } else if (t instanceof Error) {
273: throw (Error) t;
274: } else {
275: throw new Error("Unexpected invocation error", t);
276: }
277: } catch (IllegalAccessException iae) {
278: // this can't happen
279: throw new Error("Unexpected invocation error", iae);
280: }
281: }
282:
283: private static Method getTrampoline() {
284: Method tramp = null;
285:
286: try {
287: tramp = (Method) AccessController
288: .doPrivileged(new PrivilegedExceptionAction() {
289: public Object run() throws Exception {
290: Class[] types;
291: Class t = getTrampolineClass();
292: Method b;
293:
294: types = new Class[] { Method.class,
295: Object.class, Object[].class };
296: b = t.getDeclaredMethod("invoke", types);
297: ((AccessibleObject) b).setAccessible(true);
298: return b;
299: }
300: });
301: } catch (Exception e) {
302: throw new InternalError("bouncer cannot be found");
303: }
304: return tramp;
305: }
306:
307: protected synchronized Class loadClass(String name, boolean resolve)
308: throws ClassNotFoundException {
309: // First, check if the class has already been loaded
310: ReflectUtil.checkPackageAccess(name);
311: Class c = findLoadedClass(name);
312: if (c == null) {
313: try {
314: c = findClass(name);
315: } catch (ClassNotFoundException e) {
316: // Fall through ...
317: }
318: if (c == null) {
319: c = getParent().loadClass(name);
320: }
321: }
322: if (resolve) {
323: resolveClass(c);
324: }
325: return c;
326: }
327:
328: protected Class findClass(final String name)
329: throws ClassNotFoundException {
330: if (!name.startsWith(MISC_PKG)) {
331: throw new ClassNotFoundException(name);
332: }
333: String path = name.replace('.', '/').concat(".class");
334: URL res = getResource(path);
335: if (res != null) {
336: try {
337: return defineClass(name, res);
338: } catch (IOException e) {
339: throw new ClassNotFoundException(name, e);
340: }
341: } else {
342: throw new ClassNotFoundException(name);
343: }
344: }
345:
346: /*
347: * Define the proxy classes
348: */
349: private Class defineClass(String name, URL url) throws IOException {
350: byte[] b = getBytes(url);
351: CodeSource cs = new CodeSource(null,
352: (java.security.cert.Certificate[]) null);
353: if (!name.equals(TRAMPOLINE)) {
354: throw new IOException("MethodUtil: bad name " + name);
355: }
356: return defineClass(name, b, 0, b.length, cs);
357: }
358:
359: /*
360: * Returns the contents of the specified URL as an array of bytes.
361: */
362: private static byte[] getBytes(URL url) throws IOException {
363: URLConnection uc = url.openConnection();
364: if (uc instanceof java.net.HttpURLConnection) {
365: java.net.HttpURLConnection huc = (java.net.HttpURLConnection) uc;
366: int code = huc.getResponseCode();
367: if (code >= java.net.HttpURLConnection.HTTP_BAD_REQUEST) {
368: throw new IOException("open HTTP connection failed.");
369: }
370: }
371: int len = uc.getContentLength();
372: InputStream in = new BufferedInputStream(uc.getInputStream());
373:
374: byte[] b;
375: try {
376: if (len != -1) {
377: // Read exactly len bytes from the input stream
378: b = new byte[len];
379: while (len > 0) {
380: int n = in.read(b, b.length - len, len);
381: if (n == -1) {
382: throw new IOException("unexpected EOF");
383: }
384: len -= n;
385: }
386: } else {
387: b = new byte[8192];
388: int total = 0;
389: while ((len = in.read(b, total, b.length - total)) != -1) {
390: total += len;
391: if (total >= b.length) {
392: byte[] tmp = new byte[total * 2];
393: System.arraycopy(b, 0, tmp, 0, total);
394: b = tmp;
395: }
396: }
397: // Trim array to correct size, if necessary
398: if (total != b.length) {
399: byte[] tmp = new byte[total];
400: System.arraycopy(b, 0, tmp, 0, total);
401: b = tmp;
402: }
403: }
404: } finally {
405: in.close();
406: }
407: return b;
408: }
409:
410: protected PermissionCollection getPermissions(CodeSource codesource) {
411: PermissionCollection perms = super .getPermissions(codesource);
412: perms.add(new AllPermission());
413: return perms;
414: }
415:
416: private static Class getTrampolineClass() {
417: try {
418: return Class.forName(TRAMPOLINE, true, new MethodUtil());
419: } catch (ClassNotFoundException e) {
420: }
421: return null;
422: }
423:
424: }
|