提交 2479e6a1 authored 作者: Thomas Mueller's avatar Thomas Mueller

The database now tries to detect if the classloader or virtual machine has almost shut down.

上级 c93e30ee
......@@ -60,6 +60,7 @@ import org.h2.util.ClassUtils;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.IntHashMap;
import org.h2.util.MemoryUtils;
import org.h2.util.NetUtils;
import org.h2.util.ObjectArray;
import org.h2.util.SmallLRUCache;
......@@ -1033,6 +1034,14 @@ public class Database implements DataHandler {
if (closing) {
return;
}
if (MemoryUtils.isShutdown()) {
try {
traceSystem.getTrace(Trace.DATABASE).error("already shut down");
} catch (Exception e) {
// ignore
}
return;
}
closing = true;
stopServer();
if (userSessions.size() > 0) {
......
......@@ -8,6 +8,8 @@ package org.h2.engine;
import java.lang.ref.WeakReference;
import org.h2.message.Trace;
/**
* This class is responsible to close a database if the application did not
* close a connection. A database closer object only exists if there is no user
......@@ -16,6 +18,7 @@ import java.lang.ref.WeakReference;
public class DatabaseCloser extends Thread {
private final boolean shutdownHook;
private final Trace trace;
private volatile WeakReference databaseRef;
private int delayInMillis;
private boolean stopImmediately;
......@@ -24,6 +27,7 @@ public class DatabaseCloser extends Thread {
this.databaseRef = new WeakReference(db);
this.delayInMillis = delayInMillis;
this.shutdownHook = shutdownHook;
trace = db.getTrace(Trace.DATABASE);
}
/**
......@@ -72,7 +76,20 @@ public class DatabaseCloser extends Thread {
}
}
if (database != null) {
try {
database.close(shutdownHook);
} catch (RuntimeException e) {
// this can happen when stopping a web application,
// if loading classes is no longer allowed
// it would throw an IllegalStateException
try {
trace.error("Could not close the database", e);
// if this was successful, we ignore the exception
// otherwise not
} catch (RuntimeException e2) {
throw e;
}
}
}
}
......
......@@ -75,4 +75,8 @@ public class JdbcDriverUtils {
}
}
static boolean isShutdown() {
return DRIVERS == null;
}
}
......@@ -80,4 +80,15 @@ public class MemoryUtils {
reserveMemory = null;
}
/**
* Check if the classloader or virtual machine is shut down. In this case
* static references are set to null, which can cause NullPointerExceptions
* and can be confusing because it looks like a bug in the application.
*
* @return true if static references are set to null
*/
public static boolean isShutdown() {
return StringCache.isShutdown() || JdbcDriverUtils.isShutdown() || Resources.isShutdown();
}
}
......@@ -49,9 +49,9 @@ public class DataType {
*/
public static final int TYPE_DATALINK = 70;
private static ObjectArray types = new ObjectArray();
private static HashMap typesByName = new HashMap();
private static DataType[] typesByValueType = new DataType[Value.TYPE_COUNT];
private static final ObjectArray TYPES = new ObjectArray();
private static final HashMap TYPES_BY_NAME = new HashMap();
private static final DataType[] TYPES_BY_VALUE_TYPE = new DataType[Value.TYPE_COUNT];
/**
* The value type of this data type.
......@@ -318,8 +318,8 @@ public class DataType {
new String[]{"RESULT_SET"},
20
);
for (int i = 0; i < typesByValueType.length; i++) {
DataType dt = typesByValueType[i];
for (int i = 0; i < TYPES_BY_VALUE_TYPE.length; i++) {
DataType dt = TYPES_BY_VALUE_TYPE[i];
if (dt == null) {
throw Message.getInternalError("unmapped type " + i);
}
......@@ -350,17 +350,17 @@ public class DataType {
dt.caseSensitive = dataType.caseSensitive;
dt.hidden = i > 0;
dt.memory = memory;
for (int j = 0; j < types.size(); j++) {
DataType t2 = (DataType) types.get(j);
for (int j = 0; j < TYPES.size(); j++) {
DataType t2 = (DataType) TYPES.get(j);
if (t2.sqlType == dt.sqlType) {
dt.sqlTypePos++;
}
}
typesByName.put(dt.name, dt);
if (typesByValueType[type] == null) {
typesByValueType[type] = dt;
TYPES_BY_NAME.put(dt.name, dt);
if (TYPES_BY_VALUE_TYPE[type] == null) {
TYPES_BY_VALUE_TYPE[type] = dt;
}
types.add(dt);
TYPES.add(dt);
}
}
......@@ -412,7 +412,7 @@ public class DataType {
* @return the list
*/
public static ObjectArray getTypes() {
return types;
return TYPES;
}
/**
......@@ -639,9 +639,9 @@ public class DataType {
* @return the data type object
*/
public static DataType getDataType(int type) {
DataType dt = typesByValueType[type];
DataType dt = TYPES_BY_VALUE_TYPE[type];
if (dt == null) {
dt = typesByValueType[Value.NULL];
dt = TYPES_BY_VALUE_TYPE[Value.NULL];
}
return dt;
}
......@@ -857,7 +857,7 @@ public class DataType {
* @return the data type object
*/
public static DataType getTypeByName(String s) {
return (DataType) typesByName.get(s);
return (DataType) TYPES_BY_NAME.get(s);
}
/**
......
......@@ -30,20 +30,17 @@ public class ValueInt extends Value {
*/
public static final int DISPLAY_SIZE = 11;
private static final int STATIC_SIZE = 100;
private static final int STATIC_SIZE = 128;
// must be a power of 2
private static final int DYNAMIC_SIZE = 256;
// TODO check performance of final static
private static ValueInt[] staticCache;
private static ValueInt[] dynamicCache;
private static final ValueInt[] STATIC_CACHE = new ValueInt[STATIC_SIZE];
private static final ValueInt[] DYNAMIC_CACHE = new ValueInt[DYNAMIC_SIZE];
private final int value;
static {
staticCache = new ValueInt[STATIC_SIZE];
dynamicCache = new ValueInt[DYNAMIC_SIZE];
for (int i = 0; i < STATIC_SIZE; i++) {
staticCache[i] = new ValueInt(i);
STATIC_CACHE[i] = new ValueInt(i);
}
}
......@@ -59,12 +56,12 @@ public class ValueInt extends Value {
*/
public static ValueInt get(int i) {
if (i >= 0 && i < STATIC_SIZE) {
return staticCache[i];
return STATIC_CACHE[i];
}
ValueInt v = dynamicCache[i & DYNAMIC_SIZE - 1];
ValueInt v = DYNAMIC_CACHE[i & (DYNAMIC_SIZE - 1)];
if (v == null || v.value != i) {
v = new ValueInt(i);
dynamicCache[i & DYNAMIC_SIZE - 1] = v;
DYNAMIC_CACHE[i & (DYNAMIC_SIZE - 1)] = v;
}
return v;
}
......
......@@ -31,17 +31,17 @@ public class ValueLong extends Value {
*/
public static final int DISPLAY_SIZE = 20;
private static final int STATIC_SIZE = 10;
private static ValueLong[] cache;
private static final int STATIC_SIZE = 100;
private static final ValueLong[] STATIC_CACHE;
private static final BigInteger MIN = new BigInteger("" + Long.MIN_VALUE);
private static final BigInteger MAX = new BigInteger("" + Long.MAX_VALUE);
private final long value;
static {
cache = new ValueLong[STATIC_SIZE];
STATIC_CACHE = new ValueLong[STATIC_SIZE];
for (int i = 0; i < STATIC_SIZE; i++) {
cache[i] = new ValueLong(i);
STATIC_CACHE[i] = new ValueLong(i);
}
}
......@@ -188,7 +188,7 @@ public class ValueLong extends Value {
*/
public static ValueLong get(long i) {
if (i >= 0 && i < STATIC_SIZE) {
return cache[(int) i];
return STATIC_CACHE[(int) i];
}
return (ValueLong) Value.cache(new ValueLong(i));
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论