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