1 // Copyright 2010 Google Inc. All Rights Reserved.
3 package com
.google
.apphosting
.api
;
5 import sun
.misc
.Unsafe
;
7 import java
.lang
.reflect
.Field
;
8 import java
.security
.AccessController
;
9 import java
.security
.PrivilegedAction
;
10 import java
.security
.PrivilegedExceptionAction
;
11 import java
.util
.HashSet
;
15 * Provides special reflection utilities to frameworks running on Google App
16 * Engine. Most applications will not need to use this class. The
17 * utilities provided by this class are similar to those provided by the
18 * class {@link sun.misc.Unsafe}.
20 * As of this writing we only expose one method:
21 * {@link #allocateInstance(Class)}. This is needed by GWT for their
22 * serialization strategy.
25 public final class ReflectionUtils
{
27 private static final String USER_CLASSLOADER_PROD
=
28 "com.google.apphosting.runtime.security.UserClassLoader";
30 private static final String USER_CLASS_LOADER_DEV
=
31 "com.google.appengine.tools.development.IsolatedAppClassLoader";
33 private ReflectionUtils(){}
35 private static Unsafe theUnsafe
;
38 * We only support the method {@link #allocateInstance(Class)} on user classes
39 * and a small set of exceptions.
41 private static final Set
<String
> allocateInstanceExceptionSet
;
45 AccessController
.doPrivileged(new PrivilegedExceptionAction
<Object
>() {
46 public Object
run() throws NoSuchFieldException
, IllegalAccessException
{
47 Class
<?
> klass
= sun
.misc
.Unsafe
.class;
48 Field field
= klass
.getDeclaredField("theUnsafe");
49 field
.setAccessible(true);
50 theUnsafe
= (sun
.misc
.Unsafe
) field
.get(null);
55 allocateInstanceExceptionSet
= new HashSet
<String
>(2);
56 allocateInstanceExceptionSet
.add(Object
.class.getName());
57 allocateInstanceExceptionSet
.add(Number
.class.getName());
58 } catch (Throwable t
) {
59 throw new RuntimeException("Error initializing com.google.apphosting.api.ReflectionUtils", t
);
64 * Allocate an instance of a given class but do not run any constructor.
65 * This can be useful to frameworks implementing serialization. This method
66 * only supports App Engine user classes, not JRE classes.
68 * @param klass The class
69 * @return An instance of the class
70 * @throws SecurityException if klass is not a legal class.
72 public static Object
allocateInstance(Class
<?
> klass
) throws InstantiationException
{
74 throw new NullPointerException();
76 checkFullClassHierarchyIsSafeToAllocateInstance(klass
, getUserClassLoader());
77 return theUnsafe
.allocateInstance(klass
);
81 * Retrieves the UserClassLoader in both the prod and
84 private static ClassLoader
getUserClassLoader() {
85 ClassLoader userClassLoader
= getUserClassLoader(USER_CLASSLOADER_PROD
);
86 if (null == userClassLoader
){
87 userClassLoader
= getUserClassLoader(USER_CLASS_LOADER_DEV
);
89 return userClassLoader
;
93 * Attempts to locate the "current" UserClassLoader by using the Thread
94 * contextClassLoader. This method may return null, indicating that the current
95 * thread is not an App Engine user thread.
97 private static ClassLoader
getUserClassLoader(final String classLoaderClassName
) {
98 return AccessController
.doPrivileged(new PrivilegedAction
<ClassLoader
>(){
99 public ClassLoader
run(){
100 ClassLoader current
= Thread
.currentThread().getContextClassLoader();
101 while (current
!= null) {
102 if (current
.getClass().getName().equals(classLoaderClassName
)) {
105 current
= current
.getClass().getClassLoader();
113 * Recursively checks that the provided class and all of its ancestors satisfy
114 * one of the following two conditions:
116 * <li> The class was loaded by the user class loader, or
117 * <li> {@link #isExceptionToAllocateInstanceRule(Class)} is true
119 * @param klass A non-null Class object
121 private static void checkFullClassHierarchyIsSafeToAllocateInstance(
122 Class
<?
> klass
, ClassLoader userClassLoader
) {
123 if (null == userClassLoader
){
126 if (!wasLoadedByUserClassLoader(klass
, userClassLoader
)
127 && !isExceptionToAllocateInstanceRule(klass
)) {
128 throw new SecurityException("App Engine only supports the use of the method "
129 + "ReflectionUtils.allocateInstance(Class klass) on selected classes. "
130 + "The following may not be in the type hierarchy of the supplied class: "
133 klass
= klass
.getSuperclass();
135 checkFullClassHierarchyIsSafeToAllocateInstance(klass
, userClassLoader
);
140 * Checks whether or not the provided class is an exception
141 * to our rule that we only support {@link #allocateInstance(Class)}
142 * for user classes. Currently we keep a static set of such excpetions.
143 * @param klass non-null Class object
144 * @return boolean indicating whether or not the class is an exception.
146 private static boolean isExceptionToAllocateInstanceRule(Class
<?
> klass
){
147 return allocateInstanceExceptionSet
.contains(klass
.getName());
150 private static boolean wasLoadedByUserClassLoader(final Class
<?
> klass
,
151 final ClassLoader userClassLoader
) {
152 return AccessController
.doPrivileged(new PrivilegedAction
<Boolean
>() {
153 public Boolean
run() {
154 ClassLoader currentLoader
= klass
.getClassLoader();
156 if (currentLoader
== null) {
159 if (currentLoader
== userClassLoader
) {
162 currentLoader
= currentLoader
.getClass().getClassLoader();