/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.unit;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import org.h2.test.TestBase;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.value.ValueInt;
/**
* Tests if Tomcat would clear static fields when re-loading a web application.
* See also
* http://svn.apache.org/repos/asf/tomcat/trunk/java/org/apache/catalina
* /loader/WebappClassLoader.java
*/
public class TestClearReferences extends TestBase {
private static final String[] KNOWN_REFRESHED = {
"org.h2.compress.CompressLZF.cachedHashTable",
"org.h2.constant.DbSettings.defaultSettings",
"org.h2.engine.SessionRemote.sessionFactory",
"org.h2.jdbcx.JdbcDataSourceFactory.cachedTraceSystem",
"org.h2.store.RecoverTester.instance",
"org.h2.store.fs.FilePath.providers",
"org.h2.store.fs.FilePath.tempRandom",
"org.h2.tools.CompressTool.cachedBuffer",
"org.h2.util.CloseWatcher.queue",
"org.h2.util.CloseWatcher.refs",
"org.h2.util.DateTimeUtils.cachedCalendar",
"org.h2.util.MathUtils.cachedSecureRandom",
"org.h2.util.NetUtils.cachedLocalAddress",
"org.h2.util.StringUtils.softCache",
"org.h2.util.Utils.allowedClassNames",
"org.h2.util.Utils.allowedClassNamePrefixes",
"org.h2.value.CompareMode.lastUsed",
"org.h2.value.Value.softCache",
};
private boolean hasError;
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
private void clear() throws Exception {
ArrayList<Class <?>> classes = New.arrayList();
check(classes, new File("bin/org/h2"));
check(classes, new File("temp/org/h2"));
for (Class<?> clazz : classes) {
clearClass(clazz);
}
}
public void test() throws Exception {
// initialize the known classes
MathUtils.secureRandomLong();
ValueInt.get(1);
Class.forName("org.h2.store.fs.FileMemData");
clear();
if (hasError) {
fail("Tomcat may clear the field above when reloading the web app");
}
for (String s : KNOWN_REFRESHED) {
String className = s.substring(0, s.lastIndexOf('.'));
String fieldName = s.substring(s.lastIndexOf('.') + 1);
Class<?> clazz = Class.forName(className);
try {
clazz.getDeclaredField(fieldName);
} catch (Exception e) {
fail(s);
}
}
}
private void check(ArrayList<Class <?>> classes, File file) {
String name = file.getName();
if (file.isDirectory()) {
if (name.equals("CVS") || name.equals(".svn")) {
return;
}
for (File f : file.listFiles()) {
check(classes, f);
}
} else {
if (!name.endsWith(".class")) {
return;
}
String className = file.getAbsolutePath().replace('\\', '/');
className = className.substring(className.lastIndexOf("org/h2"));
String packageName = className.substring(0, className.lastIndexOf('/'));
if (!new File("src/main/" + packageName).exists()) {
return;
}
className = className.replace('/', '.');
className = className.substring(0, className.length() - ".class".length());
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
System.out.println("Could not load " + className + ": " + e.toString());
}
if (clazz != null) {
classes.add(clazz);
}
}
}
/**
* This is how Tomcat resets the fields as of 2009-01-30.
*
* @param clazz the class to clear
*/
private void clearClass(Class<?> clazz) throws Exception {
for (Field field : clazz.getDeclaredFields()) {
if (field.getType().isPrimitive() || field.getName().indexOf("$") != -1) {
continue;
}
int modifiers = field.getModifiers();
if (!Modifier.isStatic(modifiers)) {
continue;
}
field.setAccessible(true);
Object o = field.get(null);
if (o == null) {
continue;
}
if (Modifier.isFinal(modifiers)) {
if (field.getType().getName().startsWith("java.")) {
continue;
}
if (field.getType().getName().startsWith("javax.")) {
continue;
}
clearInstance(o);
} else {
clearField(clazz.getName() + "." + field.getName() + " = " + o);
}
}
}
private void clearInstance(Object instance) throws Exception {
for (Field field : instance.getClass().getDeclaredFields()) {
if (field.getType().isPrimitive() || (field.getName().indexOf("$") != -1)) {
continue;
}
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) {
continue;
}
field.setAccessible(true);
Object o = field.get(instance);
if (o == null) {
continue;
}
// loadedByThisOrChild
if (o.getClass().getName().startsWith("java.lang.")) {
continue;
}
if (o.getClass().isArray() && o.getClass().getComponentType().isPrimitive()) {
continue;
}
clearField(instance.getClass().getName() + "." + field.getName() + " = " + o);
}
}
private void clearField(String s) {
for (String k : KNOWN_REFRESHED) {
if (s.startsWith(k)) {
return;
}
}
hasError = true;
System.out.println(s);
}
}