1 // Copyright 2009 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.tools
.development
.agent
.runtime
;
5 import com
.google
.appengine
.tools
.development
.agent
.AppEngineDevAgent
;
6 import com
.google
.appengine
.tools
.development
.agent
.impl
.Agent
;
7 import com
.google
.apphosting
.utils
.clearcast
.ClearCast
;
9 import sun
.reflect
.Reflection
;
11 import java
.lang
.reflect
.AccessibleObject
;
12 import java
.lang
.reflect
.Constructor
;
13 import java
.lang
.reflect
.Field
;
14 import java
.lang
.reflect
.InvocationTargetException
;
15 import java
.lang
.reflect
.Member
;
16 import java
.lang
.reflect
.Method
;
17 import java
.lang
.reflect
.Modifier
;
18 import java
.security
.AccessController
;
19 import java
.security
.PrivilegedAction
;
20 import java
.security
.PrivilegedActionException
;
21 import java
.security
.PrivilegedExceptionAction
;
25 * Provides runtime support for
26 * {@link com.google.appengine.tools.development.agent.AppEngineDevAgent AppEngineDevAgent}.
29 public class Runtime
{
31 private static Agent agent
= ClearCast
.cast(AppEngineDevAgent
.getAgent(), Agent
.class);
32 private static Set
<String
> blackList
= agent
.getBlackList();
34 public static ClassLoader
checkParentClassLoader(ClassLoader loader
) {
35 ClassLoader systemLoader
= ClassLoader
.getSystemClassLoader();
36 return loader
!= null && loader
!= systemLoader ? loader
: Runtime
.class.getClassLoader();
39 public static void recordClassLoader(ClassLoader loader
) {
40 agent
.recordAppClassLoader(loader
);
43 public static void reject(String className
) {
44 throw new NoClassDefFoundError(className
+ " is a restricted class. Please see the Google "
45 + " App Engine developer's guide for more details.");
49 * This method is only invoked via injected bytecode.
51 * @see RuntimeHelper#checkRestricted(boolean, String, String, String) ;
53 @SuppressWarnings("unused")
54 public static void checkRestricted(boolean violationIsError
, String classStr
,
55 String callingClassStr
, String callingClassCodeSource
) {
56 if (System
.getProperty("appengine.disableRestrictedCheck") == null) {
57 RuntimeHelper
.checkRestricted(
58 violationIsError
, classStr
, callingClassStr
, callingClassCodeSource
);
62 private static boolean isBlackListed(Class
<?
> klass
) {
63 String className
= klass
.getName().replace('.', '/');
64 return blackList
.contains(className
);
67 private static Class
<?
> verifyWhiteListed(Member m
) throws IllegalAccessException
{
68 Class
<?
> klass
= m
.getDeclaringClass();
69 if (!isWhiteListed(klass
, m
)) {
70 throw new IllegalAccessException("Reflection is not allowed on " + m
);
75 private static void verifyReadable(Class
<?
> caller
, Field f
, Object target
)
76 throws IllegalAccessException
{
77 checkAccess(f
, target
, caller
);
81 private static void verifyWritable(Class
<?
> caller
, Field f
, Object target
)
82 throws IllegalAccessException
{
83 checkAccess(f
, target
, caller
);
84 Class
<?
> klass
= verifyWhiteListed(f
);
86 if (getClassLoaderPrivileged(klass
) == null) {
87 if (!Modifier
.isPublic(f
.getModifiers())) {
88 throw new IllegalAccessException("Private fields can not be set on JRE classes.");
93 private static ClassLoader
getClassLoaderPrivileged(final Class
<?
> klass
) {
94 return AccessController
.doPrivileged(new PrivilegedAction
<ClassLoader
>() {
96 public ClassLoader
run() {
97 return klass
.getClassLoader();
102 public static Object
invoke(Method method
, Object target
, Object
[] args
) throws
103 InvocationTargetException
, IllegalAccessException
{
104 checkAccess(method
, target
, getImmediateCallerClassPrivileged());
105 verifyWhiteListed(method
);
106 AccessibilityModifier am
= new AccessibilityModifier(method
);
108 return method
.invoke(target
, args
);
114 private static Object
newInstance_(Class
<?
> callerClass
, Constructor
<?
> cons
, Object
[] args
)
115 throws InstantiationException
, IllegalAccessException
, InvocationTargetException
{
116 checkAccess(cons
, null, callerClass
);
117 verifyWhiteListed(cons
);
118 AccessibilityModifier am
= new AccessibilityModifier(cons
);
120 return cons
.newInstance(args
);
126 public static Object
newInstance(Constructor
<?
> cons
, Object
[] args
)
127 throws InstantiationException
, IllegalAccessException
, InvocationTargetException
{
128 return newInstance_(getImmediateCallerClassPrivileged(), cons
, args
);
131 public static Object
newInstance(final Class
<?
> klass
) throws InstantiationException
,
132 IllegalAccessException
{
134 Constructor
<?
> cons
= AccessController
.doPrivileged(
135 new PrivilegedExceptionAction
<Constructor
<?
>>() {
137 public Constructor
<?
> run() throws NoSuchMethodException
{
138 return klass
.getDeclaredConstructor();
141 return newInstance_(getImmediateCallerClassPrivileged(), cons
, new Object
[0]);
142 } catch (PrivilegedActionException e
) {
143 Throwable t
= e
.getCause();
144 if (t
instanceof NoSuchMethodException
) {
145 throw new InstantiationException(t
.getMessage());
147 throw new RuntimeException(t
);
148 } catch (InvocationTargetException e
) {
149 throwAsUnchecked(e
.getCause());
150 throw new InstantiationException((e
.getCause().getMessage()));
154 public static Object
get(final Field f
, final Object obj
)
155 throws IllegalAccessException
, IllegalArgumentException
{
156 return verifyAndRun(f
, obj
, Op
.Get
, new Action
<Object
>() {
158 public Object
run(Field f
, Object target
) throws IllegalAccessException
{
159 return f
.get(target
);
164 public static boolean getBoolean(final Field f
, final Object obj
)
165 throws IllegalAccessException
, IllegalArgumentException
{
166 return verifyAndRun(f
, obj
, Op
.Get
, new Action
<Boolean
>() {
168 public Boolean
run(Field f
, Object target
) throws IllegalAccessException
{
169 return f
.getBoolean(target
);
174 public static byte getByte(final Field f
, final Object obj
)
175 throws IllegalAccessException
, IllegalArgumentException
{
176 return verifyAndRun(f
, obj
, Op
.Get
, new Action
<Byte
>() {
178 public Byte
run(Field f
, Object target
) throws IllegalAccessException
{
179 return f
.getByte(target
);
184 public static char getChar(final Field f
, final Object obj
)
185 throws IllegalAccessException
, IllegalArgumentException
{
186 return verifyAndRun(f
, obj
, Op
.Get
, new Action
<Character
>() {
188 public Character
run(Field f
, Object target
) throws IllegalAccessException
{
189 return f
.getChar(target
);
194 public static double getDouble(final Field f
, final Object obj
)
195 throws IllegalAccessException
, IllegalArgumentException
{
196 return verifyAndRun(f
, obj
, Op
.Get
, new Action
<Double
>() {
198 public Double
run(Field f
, Object target
) throws IllegalAccessException
{
199 return f
.getDouble(target
);
204 public static float getFloat(final Field f
, final Object obj
)
205 throws IllegalAccessException
, IllegalArgumentException
{
206 return verifyAndRun(f
, obj
, Op
.Get
, new Action
<Float
>() {
208 public Float
run(Field f
, Object target
) throws IllegalAccessException
{
209 return f
.getFloat(target
);
214 public static int getInt(final Field f
, final Object obj
)
215 throws IllegalAccessException
, IllegalArgumentException
{
216 return verifyAndRun(f
, obj
, Op
.Get
, new Action
<Integer
>() {
218 public Integer
run(Field f
, Object target
) throws IllegalAccessException
{
219 return f
.getInt(target
);
224 public static long getLong(final Field f
, final Object obj
)
225 throws IllegalAccessException
, IllegalArgumentException
{
226 return verifyAndRun(f
, obj
, Op
.Get
, new Action
<Long
>() {
228 public Long
run(Field f
, Object target
) throws IllegalAccessException
{
229 return f
.getLong(target
);
234 public static short getShort(final Field f
, final Object obj
)
235 throws IllegalAccessException
, IllegalArgumentException
{
236 return verifyAndRun(f
, obj
, Op
.Get
, new Action
<Short
>() {
238 public Short
run(Field f
, Object target
) throws IllegalAccessException
{
239 return f
.getShort(target
);
244 public static void set(Field f
, Object obj
, final Object value
)
245 throws IllegalAccessException
, IllegalArgumentException
{
246 verifyAndRun(f
, obj
, Op
.Set
, new Action
<Object
>() {
248 public Object
run(Field f
, Object target
) throws IllegalAccessException
{
249 f
.set(target
, value
);
255 public static void setBoolean(Field f
, Object obj
, final boolean value
)
256 throws IllegalAccessException
, IllegalArgumentException
{
257 verifyAndRun(f
, obj
, Op
.Set
, new Action
<Object
>() {
259 public Object
run(Field f
, Object target
) throws IllegalAccessException
{
260 f
.setBoolean(target
, value
);
266 public static void setByte(Field f
, Object obj
, final byte value
)
267 throws IllegalAccessException
, IllegalArgumentException
{
268 verifyAndRun(f
, obj
, Op
.Set
, new Action
<Object
>() {
270 public Object
run(Field f
, Object target
) throws IllegalAccessException
{
271 f
.setByte(target
, value
);
277 public static void setChar(Field f
, Object obj
, final char value
)
278 throws IllegalAccessException
, IllegalArgumentException
{
279 verifyAndRun(f
, obj
, Op
.Set
, new Action
<Object
>() {
281 public Object
run(Field f
, Object target
) throws IllegalAccessException
{
282 f
.setChar(target
, value
);
288 public static void setDouble(Field f
, Object obj
, final double value
)
289 throws IllegalAccessException
, IllegalArgumentException
{
290 verifyAndRun(f
, obj
, Op
.Set
, new Action
<Object
>() {
292 public Object
run(Field f
, Object target
) throws IllegalAccessException
{
293 f
.setDouble(target
, value
);
299 public static void setFloat(Field f
, Object obj
, final float value
)
300 throws IllegalAccessException
, IllegalArgumentException
{
301 verifyAndRun(f
, obj
, Op
.Set
, new Action
<Object
>() {
303 public Object
run(Field f
, Object target
) throws IllegalAccessException
{
304 f
.setFloat(target
, value
);
310 public static void setInt(Field f
, Object obj
, final int value
)
311 throws IllegalAccessException
, IllegalArgumentException
{
312 verifyAndRun(f
, obj
, Op
.Set
, new Action
<Object
>() {
314 public Object
run(Field f
, Object target
) throws IllegalAccessException
{
315 f
.setInt(target
, value
);
321 public static void setLong(Field f
, Object obj
, final long value
)
322 throws IllegalAccessException
, IllegalArgumentException
{
323 verifyAndRun(f
, obj
, Op
.Set
, new Action
<Object
>() {
325 public Object
run(Field f
, Object target
) throws IllegalAccessException
{
326 f
.setLong(target
, value
);
332 public static void setShort(Field f
, Object obj
, final short value
)
333 throws IllegalAccessException
, IllegalArgumentException
{
334 verifyAndRun(f
, obj
, Op
.Set
, new Action
<Object
>() {
336 public Object
run(Field f
, Object target
) throws IllegalAccessException
{
337 f
.setShort(target
, value
);
348 private interface Action
<T
> {
349 public T
run(Field f
, Object target
) throws IllegalAccessException
;
352 private static <T
> T
verifyAndRun(Field f
, Object target
, Op op
, Action
<T
> action
)
353 throws IllegalAccessException
{
354 Class
<?
> userCaller
= getCallerClassPrivileged(3);
356 verifyReadable(userCaller
, f
, target
);
358 verifyWritable(userCaller
, f
, target
);
361 AccessibilityModifier am
= new AccessibilityModifier(f
);
363 return action
.run(f
, target
);
369 private static boolean isWhiteListed(Class
<?
> klass
, Member member
) {
374 if (isWhiteListed_(klass
, member
)) {
378 if (member
instanceof Constructor
) {
382 Class
<?
>[] interfaces
= klass
.getInterfaces();
384 for (Class
<?
> i
: interfaces
) {
385 if (isWhiteListed(i
, member
)) {
390 Class
<?
> parentClass
= klass
.getSuperclass();
391 return isWhiteListed(parentClass
, member
);
394 private static boolean isWhiteListed_(Class
<?
> klass
, Member member
) {
395 if (isBlackListed(klass
)) {
398 if (klass
== member
.getDeclaringClass()) {
401 return hasMember(klass
, member
);
404 private static boolean hasMember(Class
<?
> klass
, Member member
) {
405 if (member
instanceof Method
) {
406 Method m
= (Method
) member
;
408 klass
.getDeclaredMethod(m
.getName(), m
.getParameterTypes());
410 } catch (NoSuchMethodException e
) {
415 if (member
instanceof Constructor
) {
416 Constructor
<?
> constructor
= (Constructor
<?
>) member
;
418 klass
.getDeclaredConstructor(constructor
.getParameterTypes());
420 } catch (NoSuchMethodException e
) {
425 Field field
= (Field
) member
;
427 klass
.getDeclaredField(field
.getName());
429 } catch (NoSuchFieldException e
) {
434 private static Class
<?
> getImmediateCallerClassPrivileged() {
435 return getCallerClassPrivileged(3);
438 private static Class
<?
> getCallerClassPrivileged(final int depth
) {
439 return AccessController
.doPrivileged(new PrivilegedAction
<Class
<?
>>() {
441 public Class
<?
> run() {
442 return Reflection
.getCallerClass(depth
+ 4);
447 private static <T
extends AccessibleObject
& Member
> void checkAccess(final T m
,
448 final Object target
, Class
<?
> caller
)
449 throws IllegalAccessException
{
450 if (m
.isAccessible()) {
453 checkAccess(caller
, m
.getDeclaringClass(), target
, m
.getModifiers());
456 private static void checkAccess(final Class
<?
> caller
, final Class
<?
> member
, final Object target
,
457 final int modifiers
) throws IllegalAccessException
{
459 AccessController
.doPrivileged(new PrivilegedExceptionAction
<Object
>() {
461 public Object
run() throws IllegalAccessException
{
462 Reflection
.ensureMemberAccess(caller
, member
, target
, modifiers
);
466 } catch (PrivilegedActionException e
) {
467 Throwable t
= e
.getCause();
468 if (t
instanceof IllegalAccessException
) {
469 throw (IllegalAccessException
) t
;
471 throw new RuntimeException(t
);
475 public static void throwAsUnchecked(Throwable t
) {
476 Runtime
.<RuntimeException
>throwException_(t
);
479 @SuppressWarnings("unchecked")
480 private static <T
extends Throwable
> void throwException_(Throwable t
) throws T
{
484 static class AccessibilityModifier
{
485 private final AccessibleObject obj
;
486 private final boolean originalAccessibility
;
488 AccessibilityModifier(AccessibleObject obj
) {
490 originalAccessibility
= obj
.isAccessible();
491 setAccessible_(true);
494 public void restore() {
495 setAccessible_(originalAccessibility
);
498 private void setAccessible_(final boolean flag
) {
499 AccessController
.doPrivileged(new PrivilegedAction
<Object
>() {
501 public Object
run() {
502 obj
.setAccessible(flag
);