/**
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.apphosting.tests.usercode.testservlets.security;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.AccessibleObject;
/**
* Tests reflection in dev_appserver.
*
*/
public class TestReflection implements Runnable {
// NB(tobyr) Much of this code is copied from HackTest. We need to find a
// way to unify these tests. Meanwhile keep the tests in sync as much as
// is reasonable.
private static class Reflectee {
public static final String STRING = "string";
public static final int INT = 10;
@SuppressWarnings({"UnusedDeclaration"})
private final Object privateField = null;
final Object packageField = null;
protected final Object protectedField = null;
// Different access constructors for reflection access tests.
@SuppressWarnings({"UnusedDeclaration"})
public Reflectee(String s) {}
Reflectee() {}
@SuppressWarnings({"UnusedDeclaration"})
protected Reflectee(float f) {}
@SuppressWarnings({"UnusedDeclaration"})
private Reflectee(int unused) {}
public int publicGetInt() {
return INT;
}
@SuppressWarnings({"UnusedDeclaration"})
private String privateGetString() {
return STRING;
}
}
private static class Reflectee2 {
// Include a field of each basic type.
public boolean booleanField;
public byte byteField;
public char charField;
public short shortField;
public int intField;
public long longField;
public float floatField;
public double doubleField;
public Object objectField;
}
private static class Reflectee3Base {
public static class PublicInherited {}
}
private static class Reflectee3 extends Reflectee3Base {
public static Class constructorLocalClass;
public static Class constructorAnonymousClass;
static {
// make sure the static members are assigned
new Reflectee3();
}
@SuppressWarnings({"InstantiatingObjectToGetClassObject"})
private Reflectee3() {
class ConstructorNested {}
constructorLocalClass = ConstructorNested.class;
constructorAnonymousClass = new Object() {}.getClass();
}
public static class PublicNested {}
static class PackagedNested {}
private static class PrivateNested {
private static class DoubleNested {}
}
private static Class<?> getMethodLocalClass() {
class MethodNestedLocal {}
return MethodNestedLocal.class;
};
@SuppressWarnings({"InstantiatingObjectToGetClassObject"})
private static Class<?> getMethodAnonymousClass() {
return new Object() {}.getClass();
};
}
public void run() {
try {
testReflectAccessSelf();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void testReflectAccessSelf()
throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException,
InstantiationException {
// Try reflecting on self
Class<Reflectee> klass = Reflectee.class;
Constructor[] declaredConstructors = klass.getDeclaredConstructors();
assertTrue(declaredConstructors.length == 4);
Method[] declaredMethods = klass.getDeclaredMethods();
assertTrue(declaredMethods.length == 2);
Field[] declaredFields = klass.getDeclaredFields();
assertTrue(declaredFields.length == 5);
Constructor[] publicConstructors = klass.getConstructors();
assertTrue(publicConstructors.length == 1);
klass.newInstance();
Method[] publicMethods = klass.getMethods();
// 1 public method + 9 public methods inherited from java.lang.Object
assertTrue(publicMethods.length == 10);
Field[] publicFields = klass.getFields();
assertTrue(publicFields.length == 2);
Reflectee reflectee = new Reflectee();
// Try calling a public method via reflection
try {
Method m = klass.getDeclaredMethod("publicGetInt");
m.setAccessible(true);
assertEquals(Integer.valueOf(Reflectee.INT), m.invoke(reflectee));
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
// Try calling a private method without setAccessible.
try {
Method m = klass.getDeclaredMethod("privateGetString");
assertEquals(Reflectee.STRING, m.invoke(reflectee));
fail("Should not have been able to call private method.");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
// expected
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (SecurityException e) {
throw new RuntimeException(e);
}
// Try calling a private method with setAccessible.
try {
Method m = klass.getDeclaredMethod("privateGetString");
m.setAccessible(true);
AccessibleObject.setAccessible(new AccessibleObject[] {m}, true);
assertEquals(Reflectee.STRING, m.invoke(reflectee));
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (SecurityException e) {
throw new RuntimeException(e);
}
// Try calling a private constructor without setAccessible.
try {
Constructor c = klass.getDeclaredConstructor(int.class);
c.newInstance(42);
fail("Should not have been able to call private constructor.");
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
// expected
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
// Try calling a package constructor without setAccessible.
try {
Constructor c = klass.getDeclaredConstructor();
c.newInstance();
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
// Try calling a protected constructor without setAccessible.
try {
Constructor c = klass.getDeclaredConstructor(float.class);
c.newInstance(1.0f);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
// Try calling a private constructor with setAccessible.
try {
Constructor c = klass.getDeclaredConstructor(Integer.TYPE);
c.setAccessible(true);
c.newInstance(new Integer(42));
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
// Access fields of all basic types.
Reflectee2 obj = new Reflectee2();
Class klass2 = Reflectee2.class;
Field f;
f = klass2.getField("booleanField");
f.get(obj);
f.getBoolean(obj);
f = klass2.getField("byteField");
f.get(obj);
f.getByte(obj);
f = klass2.getField("charField");
f.get(obj);
f.getChar(obj);
f = klass2.getField("shortField");
f.get(obj);
f.getShort(obj);
f = klass2.getField("intField");
f.get(obj);
f.getInt(obj);
f = klass2.getField("longField");
f.get(obj);
f.getLong(obj);
f = klass2.getField("floatField");
f.get(obj);
f.getFloat(obj);
f = klass2.getField("doubleField");
f.get(obj);
f.getDouble(obj);
f = klass2.getField("objectField");
f.get(obj);
Class<?>[] publicClasses = Reflectee3.class.getClasses();
// 1 public declared, and 1 inherited
assertEquals(2, publicClasses.length);
// Just nested and inner classes - not anonymous or local
Class<?>[] declaredClasses = Reflectee3.class.getDeclaredClasses();
assertEquals(3, declaredClasses.length);
assertNotNull(Reflectee3.constructorAnonymousClass.getEnclosingConstructor());
assertNotNull(Reflectee3.constructorLocalClass.getEnclosingConstructor());
assertNotNull(Reflectee3.getMethodAnonymousClass().getEnclosingMethod());
assertNotNull(Reflectee3.getMethodLocalClass().getEnclosingMethod());
assertNotNull(Reflectee3.PrivateNested.DoubleNested.class);
}
private void assertNotNull(Object obj) {
if (obj == null) {
throw new RuntimeException("Not null: " + obj);
}
}
private void fail(String msg) {
throw new RuntimeException(msg);
}
private void assertEquals(Object o1, Object o2) {
if (o1 == null) {
if (o1 != o2) {
throw new RuntimeException(o1 + " != " + o2);
}
} else {
if (!o1.equals(o2)) {
throw new RuntimeException(o1 + " != " + o2);
}
}
}
private void assertTrue(boolean b) {
if (!b) {
throw new RuntimeException("Assertion failed.");
}
}
}