App Engine Java SDK version 1.9.8
[gae.git] / java / src / main / com / google / apphosting / api / ReflectionUtils.java
blob642366437f0a424db2f4c50ef47f1feb75b4b90a
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;
12 import java.util.Set;
14 /**
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}.
19 * <p>
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;
37 /**
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;
43 static {
44 try {
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);
51 return null;
53 });
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);
63 /**
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{
73 if (null == klass) {
74 throw new NullPointerException();
76 checkFullClassHierarchyIsSafeToAllocateInstance(klass, getUserClassLoader());
77 return theUnsafe.allocateInstance(klass);
80 /**
81 * Retrieves the UserClassLoader in both the prod and
82 * dev environments.
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;
92 /**
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)) {
103 return current;
105 current = current.getClass().getClassLoader();
107 return current;
113 * Recursively checks that the provided class and all of its ancestors satisfy
114 * one of the following two conditions:
115 * <ul>
116 * <li> The class was loaded by the user class loader, or
117 * <li> {@link #isExceptionToAllocateInstanceRule(Class)} is true
118 * </ul>
119 * @param klass A non-null Class object
121 private static void checkFullClassHierarchyIsSafeToAllocateInstance(
122 Class<?> klass, ClassLoader userClassLoader) {
123 if (null == userClassLoader){
124 return;
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: "
131 + klass.getName());
133 klass = klass.getSuperclass();
134 if (null != klass){
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();
155 while (true) {
156 if (currentLoader == null) {
157 return false;
159 if (currentLoader == userClassLoader) {
160 return true;
162 currentLoader = currentLoader.getClass().getClassLoader();