提交 cde29719 authored 作者: Thomas Mueller's avatar Thomas Mueller

Tomcat sets fields to null when unloading the web application.

上级 14664665
......@@ -24,11 +24,11 @@ Change Log
</li><li>Connection.isValid is a bit faster.
</li><li>H2 Console: The autocomplete feature has been improved a bit. It can now better
parse conditions.
</li><li>When restarting a web application in Tomcat, an exception is thrown sometimes.
In most cases this is a NullPointerException.
</li><li>When restarting a web application in Tomcat, an exception was thrown sometimes.
In most cases this was a NullPointerException. A workaround in H2 has been implemented.
The root cause of the problem is now documented in the FAQ:
Tomcat sets all static fields (final or non-final) to null when unloading a web application.
A workaround is to set the system property
A workaround is to put the h2.jar in the lib directory, or set the system property
org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES
to false.
</li></ul>
......
......@@ -46,17 +46,20 @@ Frequently Asked Questions
Usually, bugs get fixes as they are found. There is a release every few weeks.
Here is the list of known and confirmed issues:
</p>
<ul>
<li>Some problems have been found with right outer join. Internally, it is converted to left outer join, which
does not always produce the same results as other databases when used in combination with other joins.
<ul><li>Tomcat and Glassfish 3 sets all static fields (final or non-final) to null when
unloading a web application. This can cause a NullPointerException in H2 versions
1.1.107 and older, and may still not work in newer versions. Please report it if you
run into this issue. In Tomcat >= 6.0 this behavior can be disabled by setting the
system property org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES
to false, however Tomcat may then run out of memory. A known workaround is to
put the h2.jar file in the <code>lib</code> directory.
</li><li>Some problems have been found with right outer join. Internally, it is converted
to left outer join, which does not always produce the same results as other databases
when used in combination with other joins.
</li><li>When using Install4j before 4.1.4 on Linux and enabling 'pack200',
the h2*.jar becomes corrupted by the install process, causing application failure.
A workaround is to add an empty file h2*.jar.nopack next to the h2*.jar file.
This problem is solved in Install4j 4.1.4.
</li><li>Tomcat sets all static fields (final or non-final) to null when unloading a web application.
This can cause a NullPointerException in H2. In Tomcat >= 6.0 this behavior can be disabled
by setting the system property org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES
to false.
</li></ul>
<br /><a name="open_source"></a>
......
......@@ -659,7 +659,7 @@ public class Parser {
read("=");
Expression expression;
if (readIf("DEFAULT")) {
expression = ValueExpression.DEFAULT;
expression = ValueExpression.getDefault();
} else {
expression = readExpression();
}
......@@ -2344,7 +2344,7 @@ public class Parser {
break;
case NULL:
read();
r = ValueExpression.NULL;
r = ValueExpression.getNull();
break;
case VALUE:
r = ValueExpression.get(currentValue);
......@@ -2376,7 +2376,7 @@ public class Parser {
private Expression readWhen(Expression left) throws SQLException {
if (readIf("END")) {
readIf("CASE");
return ValueExpression.NULL;
return ValueExpression.getNull();
}
if (readIf("ELSE")) {
Expression elsePart = readExpression();
......
......@@ -87,7 +87,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
if (fileName == null || fileName.trim().length() == 0) {
fileName = "script.sql";
}
fileName = SysProperties.scriptDirectory + fileName;
fileName = SysProperties.getScriptDirectory() + fileName;
}
return fileName;
}
......
/*
* Copyright 2004-2009 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
* Copyright 2004-2009 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.command.dml;
......@@ -16,7 +15,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Comparator;
import org.h2.command.Parser;
import org.h2.constant.SysProperties;
import org.h2.constraint.Constraint;
......@@ -61,14 +59,6 @@ import org.h2.value.ValueString;
*/
public class ScriptCommand extends ScriptBase {
private static final Comparator TABLE_COMPARATOR = new Comparator() {
public int compare(Object o1, Object o2) {
Table t1 = (Table) o1;
Table t2 = (Table) o2;
return t1.getId() - t2.getId();
}
};
private boolean passwords;
private boolean data;
private boolean settings;
......@@ -196,7 +186,13 @@ public class ScriptCommand extends ScriptBase {
ObjectArray tables = db.getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
// sort by id, so that views are after tables and views on views
// after the base views
tables.sort(TABLE_COMPARATOR);
tables.sort(new Comparator() {
public int compare(Object o1, Object o2) {
Table t1 = (Table) o1;
Table t2 = (Table) o2;
return t1.getId() - t2.getId();
}
});
for (int i = 0; i < tables.size(); i++) {
Table table = (Table) tables.get(i);
table.lock(session, false, false);
......
......@@ -6,7 +6,7 @@
*/
package org.h2.command.dml;
import org.h2.util.ObjectArray;
import java.util.ArrayList;
/**
* The list of setting for a SET statement.
......@@ -193,56 +193,51 @@ public class SetTypes {
*/
public static final int QUERY_TIMEOUT = 36;
private static ObjectArray types = new ObjectArray();
static {
setType(IGNORECASE, "IGNORECASE");
setType(MAX_LOG_SIZE, "MAX_LOG_SIZE");
setType(MODE, "MODE");
setType(READONLY, "READONLY");
setType(LOCK_TIMEOUT, "LOCK_TIMEOUT");
setType(DEFAULT_LOCK_TIMEOUT, "DEFAULT_LOCK_TIMEOUT");
setType(DEFAULT_TABLE_TYPE, "DEFAULT_TABLE_TYPE");
setType(CACHE_SIZE, "CACHE_SIZE");
setType(TRACE_LEVEL_SYSTEM_OUT, "TRACE_LEVEL_SYSTEM_OUT");
setType(TRACE_LEVEL_FILE, "TRACE_LEVEL_FILE");
setType(TRACE_MAX_FILE_SIZE, "TRACE_MAX_FILE_SIZE");
setType(COLLATION, "COLLATION");
setType(CLUSTER, "CLUSTER");
setType(WRITE_DELAY, "WRITE_DELAY");
setType(DATABASE_EVENT_LISTENER, "DATABASE_EVENT_LISTENER");
setType(MAX_MEMORY_ROWS, "MAX_MEMORY_ROWS");
setType(LOCK_MODE, "LOCK_MODE");
setType(DB_CLOSE_DELAY, "DB_CLOSE_DELAY");
setType(LOG, "LOG");
setType(THROTTLE, "THROTTLE");
setType(MAX_MEMORY_UNDO, "MAX_MEMORY_UNDO");
setType(MAX_LENGTH_INPLACE_LOB, "MAX_LENGTH_INPLACE_LOB");
setType(COMPRESS_LOB, "COMPRESS_LOB");
setType(ALLOW_LITERALS, "ALLOW_LITERALS");
setType(MULTI_THREADED, "MULTI_THREADED");
setType(SCHEMA, "SCHEMA");
setType(OPTIMIZE_REUSE_RESULTS, "OPTIMIZE_REUSE_RESULTS");
setType(SCHEMA_SEARCH_PATH, "SCHEMA_SEARCH_PATH");
setType(UNDO_LOG, "UNDO_LOG");
setType(REFERENTIAL_INTEGRITY, "REFERENTIAL_INTEGRITY");
setType(MVCC, "MVCC");
setType(MAX_OPERATION_MEMORY, "MAX_OPERATION_MEMORY");
setType(EXCLUSIVE, "EXCLUSIVE");
setType(CREATE_BUILD, "CREATE_BUILD");
setType(VARIABLE, "@");
setType(QUERY_TIMEOUT, "QUERY_TIMEOUT");
}
private static final ArrayList TYPES = new ArrayList();
private SetTypes() {
// utility class
}
private static void setType(int type, String name) {
while (types.size() <= type) {
types.add(null);
}
types.set(type, name);
static {
ArrayList list = TYPES;
list.add(null);
list.add(IGNORECASE, "IGNORECASE");
list.add(MAX_LOG_SIZE, "MAX_LOG_SIZE");
list.add(MODE, "MODE");
list.add(READONLY, "READONLY");
list.add(LOCK_TIMEOUT, "LOCK_TIMEOUT");
list.add(DEFAULT_LOCK_TIMEOUT, "DEFAULT_LOCK_TIMEOUT");
list.add(DEFAULT_TABLE_TYPE, "DEFAULT_TABLE_TYPE");
list.add(CACHE_SIZE, "CACHE_SIZE");
list.add(TRACE_LEVEL_SYSTEM_OUT, "TRACE_LEVEL_SYSTEM_OUT");
list.add(TRACE_LEVEL_FILE, "TRACE_LEVEL_FILE");
list.add(TRACE_MAX_FILE_SIZE, "TRACE_MAX_FILE_SIZE");
list.add(COLLATION, "COLLATION");
list.add(CLUSTER, "CLUSTER");
list.add(WRITE_DELAY, "WRITE_DELAY");
list.add(DATABASE_EVENT_LISTENER, "DATABASE_EVENT_LISTENER");
list.add(MAX_MEMORY_ROWS, "MAX_MEMORY_ROWS");
list.add(LOCK_MODE, "LOCK_MODE");
list.add(DB_CLOSE_DELAY, "DB_CLOSE_DELAY");
list.add(LOG, "LOG");
list.add(THROTTLE, "THROTTLE");
list.add(MAX_MEMORY_UNDO, "MAX_MEMORY_UNDO");
list.add(MAX_LENGTH_INPLACE_LOB, "MAX_LENGTH_INPLACE_LOB");
list.add(COMPRESS_LOB, "COMPRESS_LOB");
list.add(ALLOW_LITERALS, "ALLOW_LITERALS");
list.add(MULTI_THREADED, "MULTI_THREADED");
list.add(SCHEMA, "SCHEMA");
list.add(OPTIMIZE_REUSE_RESULTS, "OPTIMIZE_REUSE_RESULTS");
list.add(SCHEMA_SEARCH_PATH, "SCHEMA_SEARCH_PATH");
list.add(UNDO_LOG, "UNDO_LOG");
list.add(REFERENTIAL_INTEGRITY, "REFERENTIAL_INTEGRITY");
list.add(MVCC, "MVCC");
list.add(MAX_OPERATION_MEMORY, "MAX_OPERATION_MEMORY");
list.add(EXCLUSIVE, "EXCLUSIVE");
list.add(CREATE_BUILD, "CREATE_BUILD");
list.add(VARIABLE, "@");
list.add(QUERY_TIMEOUT, "QUERY_TIMEOUT");
}
/**
......@@ -252,16 +247,16 @@ public class SetTypes {
* @return the number
*/
public static int getType(String name) {
for (int i = 0; i < types.size(); i++) {
if (name.equals(types.get(i))) {
for (int i = 0; i < getTypes().size(); i++) {
if (name.equals(getTypes().get(i))) {
return i;
}
}
return -1;
}
public static ObjectArray getSettings() {
return types;
public static ArrayList getTypes() {
return TYPES;
}
/**
......@@ -271,7 +266,7 @@ public class SetTypes {
* @return the name
*/
public static String getTypeName(int type) {
return (String) types.get(type);
return (String) getTypes().get(type);
}
}
......@@ -94,7 +94,7 @@ public class Update extends Prepared {
Value newValue;
if (newExpr == null) {
newValue = oldRow.getValue(i);
} else if (newExpr == ValueExpression.DEFAULT) {
} else if (newExpr == ValueExpression.getDefault()) {
Column column = table.getColumn(i);
Expression defaultExpr = column.getDefaultExpression();
Value v;
......
......@@ -45,7 +45,7 @@ public class CompressLZF implements Compressor {
private static final int MAX_OFF = 1 << 13;
private static final int MAX_REF = (1 << 8) + (1 << 3);
private int[] hashTab;
private int[] cachedHashTable;
public void setOptions(String options) {
// nothing to do
......@@ -70,11 +70,12 @@ public class CompressLZF implements Compressor {
public int compress(byte[] in, int inLen, byte[] out, int outPos) {
int inPos = 0;
if (hashTab == null) {
hashTab = new int[HASH_SIZE];
if (cachedHashTable == null) {
cachedHashTable = new int[HASH_SIZE];
} else {
System.arraycopy(EMPTY, 0, hashTab, 0, HASH_SIZE);
System.arraycopy(EMPTY, 0, cachedHashTable, 0, HASH_SIZE);
}
int[] hashTab = cachedHashTable;
int literals = 0;
int hash = first(in, inPos);
while (true) {
......
......@@ -492,14 +492,6 @@ public class SysProperties {
*/
public static boolean runFinalize = getBooleanSetting("h2.runFinalize", true);
/**
* System property <code>h2.scriptDirectory</code> (default: empty
* string).<br />
* Relative or absolute directory where the script files are stored to or
* read from.
*/
public static String scriptDirectory = getStringSetting("h2.scriptDirectory", "");
/**
* System property <code>h2.serverCachedObjects</code> (default: 64).<br />
* TCP Server: number of cached objects per session.
......@@ -566,7 +558,7 @@ public class SysProperties {
*/
public static final int COLLATOR_CACHE_SIZE = getCollatorCacheSize();
private static String baseDir = getStringSetting("h2.baseDir", null);
private static final String H2_BASE_DIR = "h2.baseDir";
private SysProperties() {
// utility class
......@@ -623,14 +615,32 @@ public class SysProperties {
if (!dir.endsWith("/")) {
dir += "/";
}
baseDir = dir;
System.setProperty(H2_BASE_DIR, dir);
}
/**
* INTERNAL
*/
public static String getBaseDir() {
return baseDir;
return getStringSetting(H2_BASE_DIR, null);
}
/**
* INTERNAL
* System property <code>h2.scriptDirectory</code> (default: empty
* string).<br />
* Relative or absolute directory where the script files are stored to or
* read from.
*/
public static String getScriptDirectory() {
return getStringSetting("h2.scriptDirectory", "");
}
/**
* INTERNAL
*/
public static void setScriptDirectory(String dir) {
System.setProperty("h2.scriptDirectory", dir);
}
/**
......
......@@ -7,10 +7,10 @@
package org.h2.engine;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import org.h2.api.DatabaseEventListener;
import org.h2.command.dml.SetTypes;
import org.h2.constant.ErrorCode;
......@@ -20,7 +20,6 @@ import org.h2.security.SHA256;
import org.h2.util.ByteUtils;
import org.h2.util.FileUtils;
import org.h2.util.MathUtils;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
/**
......@@ -44,25 +43,6 @@ public class ConnectionInfo implements Cloneable {
private boolean persistent;
private boolean unnamed;
static {
ObjectArray list = SetTypes.getSettings();
for (int i = 0; i < list.size(); i++) {
KNOWN_SETTINGS.add(list.get(i));
}
// TODO document these settings
String[] connectionTime = new String[] { "ACCESS_MODE_LOG", "ACCESS_MODE_DATA", "AUTOCOMMIT", "CIPHER",
"CREATE", "CACHE_TYPE", "DB_CLOSE_ON_EXIT", "FILE_LOCK", "IGNORE_UNKNOWN_SETTINGS", "IFEXISTS",
"PASSWORD", "RECOVER", "USER", "DATABASE_EVENT_LISTENER_OBJECT", "AUTO_SERVER",
"AUTO_RECONNECT", "OPEN_NEW" };
for (int i = 0; i < connectionTime.length; i++) {
String key = connectionTime[i];
if (SysProperties.CHECK && KNOWN_SETTINGS.contains(key)) {
Message.throwInternalError(key);
}
KNOWN_SETTINGS.add(key);
}
}
/**
* Create a connection info object.
*
......@@ -93,6 +73,30 @@ public class ConnectionInfo implements Cloneable {
parseName();
}
static {
ArrayList list = SetTypes.getTypes();
HashSet set = KNOWN_SETTINGS;
for (int i = 0; i < list.size(); i++) {
set.add(list.get(i));
}
// TODO document these settings
String[] connectionTime = new String[] { "ACCESS_MODE_LOG", "ACCESS_MODE_DATA", "AUTOCOMMIT", "CIPHER",
"CREATE", "CACHE_TYPE", "DB_CLOSE_ON_EXIT", "FILE_LOCK", "IGNORE_UNKNOWN_SETTINGS", "IFEXISTS",
"PASSWORD", "RECOVER", "USER", "DATABASE_EVENT_LISTENER_OBJECT", "AUTO_SERVER",
"AUTO_RECONNECT", "OPEN_NEW" };
for (int i = 0; i < connectionTime.length; i++) {
String key = connectionTime[i];
if (SysProperties.CHECK && set.contains(key)) {
Message.throwInternalError(key);
}
set.add(key);
}
}
private static boolean isKnownSetting(String s) {
return KNOWN_SETTINGS.contains(s);
}
public Object clone() throws CloneNotSupportedException {
ConnectionInfo clone = (ConnectionInfo) super.clone();
clone.prop = (Properties) prop.clone();
......@@ -174,7 +178,7 @@ public class ConnectionInfo implements Cloneable {
if (prop.containsKey(key)) {
throw Message.getSQLException(ErrorCode.DUPLICATE_PROPERTY_1, key);
}
if (KNOWN_SETTINGS.contains(key)) {
if (isKnownSetting(key)) {
prop.put(key, info.get(list[i]));
}
}
......@@ -195,7 +199,7 @@ public class ConnectionInfo implements Cloneable {
String value = setting.substring(equal + 1);
String key = setting.substring(0, equal);
key = StringUtils.toUpperEnglish(key);
if (!KNOWN_SETTINGS.contains(key)) {
if (!isKnownSetting(key)) {
throw Message.getSQLException(ErrorCode.UNSUPPORTED_SETTING_1, key);
}
String old = prop.getProperty(key);
......@@ -293,7 +297,7 @@ public class ConnectionInfo implements Cloneable {
* @return the value
*/
String removeProperty(String key, String defaultValue) {
if (SysProperties.CHECK && !KNOWN_SETTINGS.contains(key)) {
if (SysProperties.CHECK && !isKnownSetting(key)) {
Message.throwInternalError(key);
}
Object x = prop.remove(key);
......@@ -378,7 +382,7 @@ public class ConnectionInfo implements Cloneable {
* @return the value as a String
*/
public String getProperty(String key, String defaultValue) {
if (SysProperties.CHECK && !KNOWN_SETTINGS.contains(key)) {
if (SysProperties.CHECK && !isKnownSetting(key)) {
Message.throwInternalError(key);
}
String s = getProperty(key);
......
......@@ -60,7 +60,6 @@ import org.h2.util.CacheLRU;
import org.h2.util.ClassUtils;
import org.h2.util.FileUtils;
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;
......@@ -1007,14 +1006,6 @@ 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) {
......
......@@ -27,9 +27,8 @@ import org.h2.util.StringUtils;
public class Engine {
private static final Engine INSTANCE = new Engine();
private static final HashMap DATABASES = new HashMap();
private static volatile long wrongPasswordDelay = SysProperties.DELAY_WRONG_PASSWORD_MIN;
private static Object wrongPasswordSync = new Object();
private final HashMap databases = new HashMap();
private Engine() {
// don't allow others to instantiate
......@@ -46,7 +45,7 @@ public class Engine {
if (openNew || ci.isUnnamedInMemory()) {
database = null;
} else {
database = (Database) databases.get(name);
database = (Database) DATABASES.get(name);
}
User user = null;
boolean opened = false;
......@@ -65,7 +64,7 @@ public class Engine {
database.setMasterUser(user);
}
if (!ci.isUnnamedInMemory()) {
databases.put(name, database);
DATABASES.put(name, database);
}
database.opened();
}
......@@ -182,7 +181,7 @@ public class Engine {
* @param name the database name
*/
public void close(String name) {
databases.remove(name);
DATABASES.remove(name);
}
/**
......@@ -208,10 +207,10 @@ public class Engine {
if (delay > min && delay > 0) {
// the first correct password must be blocked,
// otherwise parallel attacks are possible
synchronized (wrongPasswordSync) {
synchronized (INSTANCE) {
// delay up to the last delay
// an attacker can't know how long it will be
delay = RandomUtils.nextInt((int) delay);
delay = RandomUtils.nextSecureInt((int) delay);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
......@@ -223,7 +222,7 @@ public class Engine {
} else {
// this method is not synchronized on the Engine, so that
// regular successful attempts are not blocked
synchronized (wrongPasswordSync) {
synchronized (INSTANCE) {
long delay = wrongPasswordDelay;
int max = SysProperties.DELAY_WRONG_PASSWORD_MAX;
if (max <= 0) {
......
......@@ -24,19 +24,6 @@ import org.h2.value.ValueString;
*/
public class MetaRecord {
private static final Comparator META_RECORD_COMPARATOR = new Comparator() {
public int compare(Object o1, Object o2) {
MetaRecord m1 = (MetaRecord) o1;
MetaRecord m2 = (MetaRecord) o2;
int c1 = DbObjectBase.getCreateOrder(m1.getObjectType());
int c2 = DbObjectBase.getCreateOrder(m2.getObjectType());
if (c1 != c2) {
return c1 - c2;
}
return m1.getId() - m2.getId();
}
};
private int id;
private int objectType;
private int headPos;
......@@ -62,7 +49,18 @@ public class MetaRecord {
* @param records the list of meta records
*/
public static void sort(ObjectArray records) {
records.sort(META_RECORD_COMPARATOR);
records.sort(new Comparator() {
public int compare(Object o1, Object o2) {
MetaRecord m1 = (MetaRecord) o1;
MetaRecord m2 = (MetaRecord) o2;
int c1 = DbObjectBase.getCreateOrder(m1.getObjectType());
int c2 = DbObjectBase.getCreateOrder(m2.getObjectType());
if (c1 != c2) {
return c1 - c2;
}
return m1.getId() - m2.getId();
}
});
}
void setRecord(SearchRow r) {
......
......@@ -76,7 +76,7 @@ public class CompareLike extends Condition {
Value l = left.getValue(session);
if (l == ValueNull.INSTANCE) {
// NULL LIKE something > NULL
return ValueExpression.NULL;
return ValueExpression.getNull();
}
}
if (escape != null) {
......@@ -89,11 +89,11 @@ public class CompareLike extends Condition {
Value r = right.getValue(session);
if (r == ValueNull.INSTANCE) {
// something LIKE NULL > NULL
return ValueExpression.NULL;
return ValueExpression.getNull();
}
Value e = escape == null ? null : escape.getValue(session);
if (e == ValueNull.INSTANCE) {
return ValueExpression.NULL;
return ValueExpression.getNull();
}
String p = r.getString();
initPattern(p, getEscapeChar(e));
......
......@@ -119,7 +119,7 @@ public class Comparison extends Condition {
private Expression getCast(Expression expr, int dataType, long precision, int scale, int displaySize, Session session)
throws SQLException {
if (expr == ValueExpression.NULL) {
if (expr == ValueExpression.getNull()) {
return expr;
}
Function function = Function.getFunction(session.getDatabase(), "CAST");
......@@ -186,10 +186,10 @@ public class Comparison extends Condition {
if (SysProperties.CHECK && (left == null || right == null)) {
Message.throwInternalError();
}
if (left == ValueExpression.NULL || right == ValueExpression.NULL) {
if (left == ValueExpression.getNull() || right == ValueExpression.getNull()) {
// TODO NULL handling: maybe issue a warning when comparing with
// a NULL constants
return ValueExpression.NULL;
return ValueExpression.getNull();
}
if (left.isConstant() && right.isConstant()) {
return ValueExpression.get(getValue(session));
......
......@@ -85,7 +85,7 @@ public class ConditionIn extends Condition {
}
left = left.optimize(session);
boolean constant = left.isConstant();
if (constant && left == ValueExpression.NULL) {
if (constant && left == ValueExpression.getNull()) {
return left;
}
boolean allValuesConstant = true;
......
......@@ -55,7 +55,7 @@ public class ConditionNot extends Condition {
if (expr.isConstant()) {
Value v = expr.getValue(session);
if (v == ValueNull.INSTANCE) {
return ValueExpression.NULL;
return ValueExpression.getNull();
}
return ValueExpression.get(v.convertTo(Value.BOOLEAN).negate());
}
......
......@@ -1621,7 +1621,7 @@ public class Function extends Expression implements FunctionCall {
displaySize = 0;
for (int i = 0; i < args.length; i++) {
Expression e = args[i];
if (e != ValueExpression.NULL && e.getType() != Value.UNKNOWN) {
if (e != ValueExpression.getNull() && e.getType() != Value.UNKNOWN) {
dataType = Value.getHigherOrder(dataType, e.getType());
scale = Math.max(scale, e.getScale());
precision = Math.max(precision, e.getPrecision());
......
......@@ -22,21 +22,39 @@ public class ValueExpression extends Expression {
/**
* The expression represents ValueNull.INSTANCE.
*/
public static final ValueExpression NULL = new ValueExpression(ValueNull.INSTANCE);
private static final Object NULL = new ValueExpression(ValueNull.INSTANCE);
/**
* This special expression represents the default value. It is used for
* UPDATE statements of the form SET COLUMN = DEFAULT. The value is
* ValueNull.INSTANCE, but should never be accessed.
*/
public static final ValueExpression DEFAULT = new ValueExpression(ValueNull.INSTANCE);
private static final Object DEFAULT = new ValueExpression(ValueNull.INSTANCE);
private Value value;
private final Value value;
private ValueExpression(Value value) {
this.value = value;
}
/**
* Get the NULL expression.
*
* @return the NULL expression
*/
public static ValueExpression getNull() {
return (ValueExpression) NULL;
}
/**
* Get the DEFAULT expression.
*
* @return the DEFAULT expression
*/
public static ValueExpression getDefault() {
return (ValueExpression) DEFAULT;
}
/**
* Create a new expression with the given value.
*
......@@ -45,7 +63,7 @@ public class ValueExpression extends Expression {
*/
public static ValueExpression get(Value value) {
if (value == ValueNull.INSTANCE) {
return ValueExpression.NULL;
return getNull();
}
return new ValueExpression(value);
}
......
......@@ -30,20 +30,18 @@ implements ObjectFactory
//## Java 1.4 end ##
{
private static TraceSystem traceSystem;
private static TraceSystem cachedTraceSystem;
private Trace trace;
static {
org.h2.Driver.load();
traceSystem = new TraceSystem(SysProperties.CLIENT_TRACE_DIRECTORY + "h2datasource" + Constants.SUFFIX_TRACE_FILE, false);
traceSystem.setLevelFile(SysProperties.DATASOURCE_TRACE_LEVEL);
}
/**
* The public constructor to create new factory objects.
*/
public JdbcDataSourceFactory() {
trace = traceSystem.getTrace("JDBCX");
trace = getTraceSystem().getTrace("JDBCX");
}
/**
......@@ -77,8 +75,12 @@ implements ObjectFactory
}
//## Java 1.4 end ##
TraceSystem getTraceSystem() {
return traceSystem;
private TraceSystem getTraceSystem() {
if (cachedTraceSystem == null) {
cachedTraceSystem = new TraceSystem(SysProperties.CLIENT_TRACE_DIRECTORY + "h2datasource" + Constants.SUFFIX_TRACE_FILE, false);
cachedTraceSystem.setLevelFile(SysProperties.DATASOURCE_TRACE_LEVEL);
}
return cachedTraceSystem;
}
Trace getTrace() {
......
......@@ -37,12 +37,6 @@ public class LogSystem {
*/
public static final int LOG_WRITTEN = -1;
private static final Comparator LOG_FILE_COMPARATOR = new Comparator() {
public int compare(Object a, Object b) {
return ((LogFile) a).getId() - ((LogFile) b).getId();
}
};
private Database database;
private ObjectArray activeLogs;
private LogFile currentLog;
......@@ -319,7 +313,11 @@ public class LogSystem {
}
}
}
activeLogs.sort(LOG_FILE_COMPARATOR);
activeLogs.sort(new Comparator() {
public int compare(Object a, Object b) {
return ((LogFile) a).getId() - ((LogFile) b).getId();
}
});
if (activeLogs.size() == 0) {
LogFile l = new LogFile(this, 0, fileNamePrefix);
activeLogs.add(l);
......
......@@ -78,7 +78,11 @@ public class Message {
}
private static String translate(String key, String[] param) {
String message = MESSAGES.getProperty(key);
String message = null;
if (MESSAGES != null) {
// Tomcat sets final static fields to null sometimes
message = MESSAGES.getProperty(key);
}
if (message == null) {
message = "(Message " + key + " not found)";
}
......
......@@ -80,18 +80,6 @@ public class DiskFile implements CacheWriter {
private static final int OFFSET = FileStore.HEADER_LENGTH;
private static final int FREE_PAGE = -1;
private static final Comparator REDO_LOG_COMPARATOR = new Comparator() {
public int compare(Object o1, Object o2) {
RedoLogRecord e1 = (RedoLogRecord) o1;
RedoLogRecord e2 = (RedoLogRecord) o2;
int comp = e1.recordId - e2.recordId;
if (comp == 0) {
comp = e1.sequenceId - e2.sequenceId;
}
return comp;
}
};
private Database database;
private String fileName;
private FileStore file;
......@@ -1203,7 +1191,17 @@ public class DiskFile implements CacheWriter {
if (redoBuffer.size() == 0) {
return;
}
redoBuffer.sort(REDO_LOG_COMPARATOR);
redoBuffer.sort(new Comparator() {
public int compare(Object o1, Object o2) {
RedoLogRecord e1 = (RedoLogRecord) o1;
RedoLogRecord e2 = (RedoLogRecord) o2;
int comp = e1.recordId - e2.recordId;
if (comp == 0) {
comp = e1.sequenceId - e2.sequenceId;
}
return comp;
}
});
// first write all deleted entries
// because delete entries are always 1 block,
// while not-deleted entries can be many blocks
......
......@@ -25,7 +25,7 @@ public class FileObjectMemory implements FileObject {
private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
private static final CompressLZF LZF = new CompressLZF();
private static final byte[] BUFFER = new byte[BLOCK_SIZE * 2];
private static final byte[] COMPRESSED_BLOCK;
private static byte[] cachedCompressedEmptyBlock;
//## Java 1.4 begin ##
private static final Cache COMPRESS_LATER = new Cache(CACHE_SIZE);
......@@ -137,11 +137,14 @@ public class FileObjectMemory implements FileObject {
}
}
static {
byte[] n = new byte[BLOCK_SIZE];
int len = LZF.compress(n, BLOCK_SIZE, BUFFER, 0);
COMPRESSED_BLOCK = new byte[len];
System.arraycopy(BUFFER, 0, COMPRESSED_BLOCK, 0, len);
static byte[] getCompressedEmptyBlock() {
if (cachedCompressedEmptyBlock == null) {
byte[] n = new byte[BLOCK_SIZE];
int len = LZF.compress(n, BLOCK_SIZE, BUFFER, 0);
cachedCompressedEmptyBlock = new byte[len];
System.arraycopy(BUFFER, 0, cachedCompressedEmptyBlock, 0, len);
}
return cachedCompressedEmptyBlock;
}
private void touch() {
......@@ -186,7 +189,7 @@ public class FileObjectMemory implements FileObject {
byte[][] n = new byte[blocks][];
System.arraycopy(data, 0, n, 0, Math.min(data.length, n.length));
for (int i = data.length; i < blocks; i++) {
n[i] = COMPRESSED_BLOCK;
n[i] = getCompressedEmptyBlock();
}
data = n;
}
......
......@@ -25,7 +25,7 @@ import org.h2.util.RandomUtils;
public class FileSystemMemory extends FileSystem {
private static final FileSystemMemory INSTANCE = new FileSystemMemory();
private final HashMap memoryFiles = new HashMap();
private static final HashMap MEMORY_FILES = new HashMap();
private FileSystemMemory() {
// don't allow construction
......@@ -42,16 +42,16 @@ public class FileSystemMemory extends FileSystem {
public void rename(String oldName, String newName) {
oldName = normalize(oldName);
newName = normalize(newName);
synchronized (memoryFiles) {
synchronized (MEMORY_FILES) {
FileObjectMemory f = getMemoryFile(oldName);
f.setName(newName);
memoryFiles.remove(oldName);
memoryFiles.put(newName, f);
MEMORY_FILES.remove(oldName);
MEMORY_FILES.put(newName, f);
}
}
public boolean createNewFile(String fileName) {
synchronized (memoryFiles) {
synchronized (MEMORY_FILES) {
if (exists(fileName)) {
return false;
}
......@@ -62,29 +62,29 @@ public class FileSystemMemory extends FileSystem {
public boolean exists(String fileName) {
fileName = normalize(fileName);
synchronized (memoryFiles) {
return memoryFiles.get(fileName) != null;
synchronized (MEMORY_FILES) {
return MEMORY_FILES.get(fileName) != null;
}
}
public void delete(String fileName) {
fileName = normalize(fileName);
synchronized (memoryFiles) {
memoryFiles.remove(fileName);
synchronized (MEMORY_FILES) {
MEMORY_FILES.remove(fileName);
}
}
public boolean tryDelete(String fileName) {
fileName = normalize(fileName);
synchronized (memoryFiles) {
memoryFiles.remove(fileName);
synchronized (MEMORY_FILES) {
MEMORY_FILES.remove(fileName);
}
return true;
}
public String createTempFile(String name, String suffix, boolean deleteOnExit, boolean inTempDir) {
name += ".";
synchronized (memoryFiles) {
synchronized (MEMORY_FILES) {
for (int i = 0;; i++) {
String n = name + (RandomUtils.getSecureLong() >>> 1) + suffix;
if (!exists(n)) {
......@@ -97,8 +97,8 @@ public class FileSystemMemory extends FileSystem {
public String[] listFiles(String path) {
ObjectArray list = new ObjectArray();
synchronized (memoryFiles) {
for (Iterator it = memoryFiles.keySet().iterator(); it.hasNext();) {
synchronized (MEMORY_FILES) {
for (Iterator it = MEMORY_FILES.keySet().iterator(); it.hasNext();) {
String name = (String) it.next();
if (name.startsWith(path)) {
list.add(name);
......@@ -112,8 +112,8 @@ public class FileSystemMemory extends FileSystem {
public void deleteRecursive(String fileName) throws SQLException {
fileName = normalize(fileName);
synchronized (memoryFiles) {
Iterator it = memoryFiles.keySet().iterator();
synchronized (MEMORY_FILES) {
Iterator it = MEMORY_FILES.keySet().iterator();
while (it.hasNext()) {
String name = (String) it.next();
if (name.startsWith(fileName)) {
......@@ -217,12 +217,12 @@ public class FileSystemMemory extends FileSystem {
private FileObjectMemory getMemoryFile(String fileName) {
fileName = normalize(fileName);
synchronized (memoryFiles) {
FileObjectMemory m = (FileObjectMemory) memoryFiles.get(fileName);
synchronized (MEMORY_FILES) {
FileObjectMemory m = (FileObjectMemory) MEMORY_FILES.get(fileName);
if (m == null) {
boolean compress = fileName.startsWith(FileSystem.PREFIX_MEMORY_LZF);
m = new FileObjectMemory(fileName, compress);
memoryFiles.put(fileName, m);
MEMORY_FILES.put(fileName, m);
}
return m;
}
......
......@@ -16,6 +16,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Locale;
import org.h2.command.Command;
......@@ -865,7 +866,7 @@ public class MetaTable extends Table {
add(rows, new String[]{"h2.recompileAlways", "" + SysProperties.RECOMPILE_ALWAYS});
add(rows, new String[]{"h2.redoBufferSize", "" + SysProperties.REDO_BUFFER_SIZE});
add(rows, new String[]{"h2.runFinalize", "" + SysProperties.runFinalize});
add(rows, new String[]{"h2.scriptDirectory", SysProperties.scriptDirectory});
add(rows, new String[]{"h2.scriptDirectory", SysProperties.getScriptDirectory()});
add(rows, new String[]{"h2.serverCachedObjects", "" + SysProperties.SERVER_CACHED_OBJECTS});
add(rows, new String[]{"h2.serverResultSetFetchSize", "" + SysProperties.SERVER_RESULT_SET_FETCH_SIZE});
add(rows, new String[]{"h2.sortNullsHigh", "" + SysProperties.SORT_NULLS_HIGH});
......@@ -887,7 +888,7 @@ public class MetaTable extends Table {
break;
}
case TYPE_INFO: {
ObjectArray types = DataType.getTypes();
ArrayList types = DataType.getTypes();
for (int i = 0; i < types.size(); i++) {
DataType t = (DataType) types.get(i);
if (t.hidden || t.sqlType == Value.NULL) {
......
......@@ -34,22 +34,22 @@ import org.h2.util.StringUtils;
*/
public class CompressTool {
private static CompressTool instance = new CompressTool();
private static byte[] buffer;
private static final int MAX_BUFFER_SIZE = 64 * 1024 * 1024;
private static final CompressTool INSTANCE = new CompressTool();
private static final int MAX_BUFFER_SIZE = 64 * 1024;
private byte[] cachedBuffer;
private CompressTool() {
// don't allow construction
}
private static byte[] getBuffer(int min) {
private byte[] getBuffer(int min) {
if (min > MAX_BUFFER_SIZE) {
return ByteUtils.newBytes(min);
}
if (buffer == null || buffer.length < min) {
buffer = ByteUtils.newBytes(min);
if (cachedBuffer == null || cachedBuffer.length < min) {
cachedBuffer = ByteUtils.newBytes(min);
}
return buffer;
return cachedBuffer;
}
/**
......@@ -58,7 +58,7 @@ public class CompressTool {
* @return the singleton
*/
public static CompressTool getInstance() {
return instance;
return INSTANCE;
}
/**
......
......@@ -23,7 +23,7 @@ import org.h2.util.StringUtils;
*/
public class MultiDimension {
private static MultiDimension instance = new MultiDimension();
private static final MultiDimension INSTANCE = new MultiDimension();
private MultiDimension() {
// don't allow construction
......@@ -35,7 +35,7 @@ public class MultiDimension {
* @return the singleton
*/
public static MultiDimension getInstance() {
return instance;
return INSTANCE;
}
/**
......
......@@ -18,11 +18,9 @@ import org.h2.store.DiskFile;
public abstract class CacheObject {
/**
* Ensure the class is loaded when initialized, so that sorting is possible
* even when loading new classes is not allowed any more. This can occur
* when stopping a web application.
* Compare cache objects by position.
*/
private static final Comparator CACHE_COMPARATOR = new Comparator() {
private static class CacheComparator implements Comparator {
public int compare(Object a, Object b) {
int pa = ((CacheObject) a).getPos();
int pb = ((CacheObject) b).getPos();
......@@ -30,6 +28,15 @@ public abstract class CacheObject {
}
};
/**
* Ensure the class is loaded when initialized, so that sorting is possible
* even when loading new classes is not allowed any more. This can occur
* when stopping a web application.
*/
static {
new CacheComparator();
}
/**
* The previous element in the LRU linked list. If the previous element is
* the head, then this element is the most recently used object.
......@@ -75,7 +82,7 @@ public abstract class CacheObject {
* @param recordList the list of cache objects
*/
public static void sort(ObjectArray recordList) {
recordList.sort(CACHE_COMPARATOR);
recordList.sort(new CacheComparator());
}
public void setBlockCount(int size) {
......
......@@ -30,7 +30,7 @@ public class DateTimeUtils {
private static final int DEFAULT_DAY = 1;
private static final int DEFAULT_HOUR = 0;
private static Calendar calendar = Calendar.getInstance();
private static Calendar cachedCalendar = Calendar.getInstance();
private DateTimeUtils() {
// utility class
......@@ -40,7 +40,14 @@ public class DateTimeUtils {
* Reset the calendar, for example after changing the default timezone.
*/
public static void resetCalendar() {
calendar = Calendar.getInstance();
cachedCalendar = null;
}
private static Calendar getCalendar() {
if (cachedCalendar == null) {
cachedCalendar = Calendar.getInstance();
}
return cachedCalendar;
}
/**
......@@ -67,7 +74,7 @@ public class DateTimeUtils {
* @return the time value without the date component
*/
public static Time cloneAndNormalizeTime(Time value) {
Calendar cal = calendar;
Calendar cal = getCalendar();
long time;
synchronized (cal) {
cal.setTime(value);
......@@ -86,7 +93,7 @@ public class DateTimeUtils {
* @return the date value at midnight
*/
public static Date cloneAndNormalizeDate(Date value) {
Calendar cal = calendar;
Calendar cal = getCalendar();
long time;
synchronized (cal) {
cal.setTime(value);
......@@ -147,7 +154,7 @@ public class DateTimeUtils {
throw Message.getInvalidValueException("calendar", null);
}
source = (Calendar) source.clone();
Calendar universal = calendar;
Calendar universal = getCalendar();
synchronized (universal) {
source.setTime(x);
convertTime(source, universal);
......@@ -334,7 +341,7 @@ public class DateTimeUtils {
private static long getTime(boolean lenient, TimeZone tz, int year, int month, int day, int hour, int minute, int second, boolean setMillis, int nano) {
Calendar c;
if (tz == null) {
c = calendar;
c = getCalendar();
} else {
c = Calendar.getInstance(tz);
}
......@@ -369,7 +376,7 @@ public class DateTimeUtils {
* @return the value
*/
public static int getDatePart(java.util.Date d, int field) {
Calendar c = calendar;
Calendar c = getCalendar();
int value;
synchronized (c) {
c.setTime(d);
......
......@@ -75,8 +75,4 @@ public class JdbcDriverUtils {
}
}
static boolean isShutdown() {
return DRIVERS == null;
}
}
......@@ -80,16 +80,4 @@ public class MemoryUtils {
reserveMemory = null;
}
/**
* Check if Tomcat has set the static references to null,
* which can cause a NullPointerException. A workaround is to
* disable the system property org.apache.catalina.loader.
* WebappClassLoader.ENABLE_CLEAR_REFERENCES
*
* @return true if static references are set to null
*/
public static boolean isShutdown() {
return StringCache.isShutdown() || JdbcDriverUtils.isShutdown() || Resources.isShutdown();
}
}
......@@ -25,28 +25,29 @@ public class RandomUtils {
/**
* The secure random object.
*/
static SecureRandom secureRandom;
static SecureRandom cachedSecureRandom;
/**
* True if the secure random object is seeded.
*/
static volatile boolean seeded;
private static Random random = new Random();
private static final Random RANDOM = new Random();
private RandomUtils() {
// utility class
}
private static synchronized SecureRandom getSecureRandom() {
if (secureRandom != null) {
return secureRandom;
if (cachedSecureRandom != null) {
return cachedSecureRandom;
}
// Workaround for SecureRandom problem as described in
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6202721
// Can not do that in a static initializer block, because
// threads are not started after the initializer block exits
// threads are not started until after the initializer block exits
try {
secureRandom = SecureRandom.getInstance("SHA1PRNG");
cachedSecureRandom = SecureRandom.getInstance("SHA1PRNG");
// On some systems, secureRandom.generateSeed() is very slow.
// In this case it is initialized using our own seed implementation
// and afterwards (in the thread) using the regular algorithm.
......@@ -55,8 +56,8 @@ public class RandomUtils {
try {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] seed = sr.generateSeed(20);
synchronized (secureRandom) {
secureRandom.setSeed(seed);
synchronized (cachedSecureRandom) {
cachedSecureRandom.setSeed(seed);
seeded = true;
}
} catch (NoSuchAlgorithmException e) {
......@@ -78,15 +79,15 @@ public class RandomUtils {
if (!seeded) {
byte[] seed = generateAlternativeSeed();
// this never reduces randomness
synchronized (secureRandom) {
secureRandom.setSeed(seed);
synchronized (cachedSecureRandom) {
cachedSecureRandom.setSeed(seed);
}
}
} catch (NoSuchAlgorithmException e) {
warn("SecureRandom", e);
secureRandom = new SecureRandom();
cachedSecureRandom = new SecureRandom();
}
return secureRandom;
return cachedSecureRandom;
}
private static byte[] generateAlternativeSeed() {
......@@ -183,15 +184,29 @@ public class RandomUtils {
return buff;
}
/**
* Get a pseudo random int value between 0 (including and the given value
* (excluding). The value is not cryptographically secure.
*
* @param lowerThan the value returned will be lower than this value
* @return the random long value
*/
public static int nextInt(int lowerThan) {
return RANDOM.nextInt(lowerThan);
}
/**
* Get a cryptographically secure pseudo random int value between 0
* (including and the given value (excluding).
*
* @param lower the value returned will be lower than this value
* @param lowerThan the value returned will be lower than this value
* @return the random long value
*/
public static int nextInt(int lower) {
return random.nextInt(lower);
public static int nextSecureInt(int lowerThan) {
SecureRandom sr = getSecureRandom();
synchronized (sr) {
return sr.nextInt(lowerThan);
}
}
/**
......
......@@ -79,8 +79,4 @@ public class Resources {
return data == null ? new byte[0] : data;
}
static boolean isShutdown() {
return FILES == null;
}
}
......@@ -120,8 +120,4 @@ public class StringCache {
softCache = new SoftReference(null);
}
static boolean isShutdown() {
return softCache == null;
}
}
......@@ -18,6 +18,7 @@ import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import org.h2.constant.ErrorCode;
......@@ -27,7 +28,6 @@ import org.h2.jdbc.JdbcBlob;
import org.h2.jdbc.JdbcClob;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.Message;
import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
import org.h2.util.StringUtils;
......@@ -49,9 +49,13 @@ public class DataType {
*/
public static final int TYPE_DATALINK = 70;
private static final ObjectArray TYPES = new ObjectArray();
/**
* The list of types. An ArrayList so that Tomcat doesn't set it to null
* when clearing references.
*/
private static final ArrayList TYPES = new ArrayList();
private static final HashMap TYPES_BY_NAME = new HashMap();
private static final DataType[] TYPES_BY_VALUE_TYPE = new DataType[Value.TYPE_COUNT];
private static final ArrayList TYPES_BY_VALUE_TYPE = new ArrayList();
/**
* The value type of this data type.
......@@ -160,6 +164,9 @@ public class DataType {
public int memory;
static {
for (int i = 0; i < Value.TYPE_COUNT; i++) {
TYPES_BY_VALUE_TYPE.add(null);
}
//## Java 1.4 begin ##
if (TYPE_BOOLEAN != Types.BOOLEAN) {
new Exception("Types.BOOLEAN: " + Types.BOOLEAN).printStackTrace();
......@@ -318,8 +325,8 @@ public class DataType {
new String[]{"RESULT_SET"},
20
);
for (int i = 0; i < TYPES_BY_VALUE_TYPE.length; i++) {
DataType dt = TYPES_BY_VALUE_TYPE[i];
for (int i = 0; i < TYPES_BY_VALUE_TYPE.size(); i++) {
DataType dt = (DataType) TYPES_BY_VALUE_TYPE.get(i);
if (dt == null) {
Message.throwInternalError("unmapped type " + i);
}
......@@ -357,8 +364,8 @@ public class DataType {
}
}
TYPES_BY_NAME.put(dt.name, dt);
if (TYPES_BY_VALUE_TYPE[type] == null) {
TYPES_BY_VALUE_TYPE[type] = dt;
if (TYPES_BY_VALUE_TYPE.get(type) == null) {
TYPES_BY_VALUE_TYPE.set(type, dt);
}
TYPES.add(dt);
}
......@@ -411,7 +418,7 @@ public class DataType {
*
* @return the list
*/
public static ObjectArray getTypes() {
public static ArrayList getTypes() {
return TYPES;
}
......@@ -639,9 +646,9 @@ public class DataType {
* @return the data type object
*/
public static DataType getDataType(int type) {
DataType dt = TYPES_BY_VALUE_TYPE[type];
DataType dt = (DataType) TYPES_BY_VALUE_TYPE.get(type);
if (dt == null) {
dt = TYPES_BY_VALUE_TYPE[Value.NULL];
dt = (DataType) TYPES_BY_VALUE_TYPE.get(Value.NULL);
}
return dt;
}
......
......@@ -329,8 +329,11 @@ public abstract class Value {
*/
static Value cache(Value v) {
if (SysProperties.OBJECT_CACHE) {
Value[] cache = (Value[]) softCache.get();
int hash = v.hashCode();
if (softCache == null) {
softCache = new SoftReference(null);
}
Value[] cache = (Value[]) softCache.get();
if (cache == null) {
cache = new Value[SysProperties.OBJECT_CACHE_SIZE];
softCache = new SoftReference(cache);
......
......@@ -25,8 +25,11 @@ public class ValueBoolean extends Value {
*/
public static final int DISPLAY_SIZE = 5;
private static final ValueBoolean TRUE = new ValueBoolean(true);
private static final ValueBoolean FALSE = new ValueBoolean(false);
/**
* Of type Object so that Tomcat doesn't set it to null.
*/
private static final Object TRUE = new ValueBoolean(true);
private static final Object FALSE = new ValueBoolean(false);
private final Boolean value;
......@@ -47,7 +50,7 @@ public class ValueBoolean extends Value {
}
public Value negate() {
return value.booleanValue() ? FALSE : TRUE;
return (ValueBoolean) (value.booleanValue() ? FALSE : TRUE);
}
public Boolean getBoolean() {
......@@ -83,7 +86,7 @@ public class ValueBoolean extends Value {
* @return the value
*/
public static ValueBoolean get(boolean b) {
return b ? TRUE : FALSE;
return (ValueBoolean) (b ? TRUE : FALSE);
}
public int getDisplaySize() {
......
......@@ -38,8 +38,8 @@ public class ValueDecimal extends Value {
private static final int DIVIDE_SCALE_ADD = 25;
private static final BigDecimal DEC_ZERO = new BigDecimal("0");
private static final BigDecimal DEC_ONE = new BigDecimal("1");
private static final ValueDecimal ZERO = new ValueDecimal(DEC_ZERO);
private static final ValueDecimal ONE = new ValueDecimal(DEC_ONE);
private static final Object ZERO = new ValueDecimal(DEC_ZERO);
private static final Object ONE = new ValueDecimal(DEC_ONE);
private final BigDecimal value;
private String valueString;
......@@ -187,9 +187,9 @@ public class ValueDecimal extends Value {
*/
public static ValueDecimal get(BigDecimal dec) {
if (DEC_ZERO.equals(dec)) {
return ZERO;
return (ValueDecimal) ZERO;
} else if (DEC_ONE.equals(dec)) {
return ONE;
return (ValueDecimal) ONE;
}
// TODO value optimization: find a way to read size of BigDecimal,
// check max cache size
......
......@@ -102,6 +102,7 @@ import org.h2.test.synth.thread.TestMulti;
import org.h2.test.unit.SelfDestructor;
import org.h2.test.unit.TestBitField;
import org.h2.test.unit.TestCache;
import org.h2.test.unit.TestClearReferences;
import org.h2.test.unit.TestCompress;
import org.h2.test.unit.TestDataPage;
import org.h2.test.unit.TestDate;
......@@ -282,6 +283,9 @@ java org.h2.test.TestAll timer
/*
include ClearReferences test case
isShutdown
PageStore.switchLogIfPossible()
drop table test;
......@@ -609,6 +613,7 @@ http://www.w3schools.com/sql/
private void testUnit() {
new TestBitField().runTest(this);
new TestCache().runTest(this);
new TestClearReferences().runTest(this);
new TestCompress().runTest(this);
new TestDataPage().runTest(this);
new TestDate().runTest(this);
......
......@@ -165,17 +165,20 @@ public class TestRandomSQL extends TestBase {
}
public void testCase(int i) throws SQLException {
String old = SysProperties.scriptDirectory;
SysProperties.scriptDirectory = "dataScript/";
seed = i;
printTime("seed: " + seed);
String old = SysProperties.getScriptDirectory();
try {
deleteDb();
} catch (SQLException e) {
processException("deleteDb", e);
SysProperties.setScriptDirectory("dataScript/");
seed = i;
printTime("seed: " + seed);
try {
deleteDb();
} catch (SQLException e) {
processException("deleteDb", e);
}
testWithSeed(bnf);
} finally {
SysProperties.setScriptDirectory(old);
}
testWithSeed(bnf);
SysProperties.scriptDirectory = old;
try {
deleteDb();
} catch (SQLException e) {
......
......@@ -52,6 +52,7 @@ I am sorry to say that, but it looks like a corruption problem. I am very intere
- Could you send the full stack trace of the exception including message text?
- What is your database URL?
- Do you use Tomcat or another web server? Do you unload or reload the web application?
- You can find out if the database is corrupted when running
SCRIPT TO 'test.sql'
- What version H2 are you using?
......
......@@ -19,6 +19,15 @@ import org.h2.test.TestBase;
*/
public class TestCache extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String[] a) throws Exception {
TestBase.createCaller().init().test();
}
public void test() throws SQLException {
if (config.memory) {
return;
......
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;
/**
* 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.util.DateTimeUtils.cachedCalendar",
"org.h2.util.StringCache.softCache",
"org.h2.value.Value.softCache",
"org.h2.jdbcx.JdbcDataSourceFactory.cachedTraceSystem",
"org.h2.compress.CompressLZF.cachedHashTable",
"org.h2.store.fs.FileObjectMemory.cachedCompressedEmptyBlock",
"org.h2.tools.CompressTool.cachedBuffer",
"org.h2.util.MemoryUtils.reserveMemory",
"org.h2.util.NetUtils.cachedLocalAddress",
"org.h2.util.RandomUtils.cachedSecureRandom"
};
private boolean hasError;
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String[] a) throws Exception {
TestBase.createCaller().init().test();
}
public void test() throws Exception {
String baseDir = "bin/org/h2";
ArrayList classes = new ArrayList();
check(classes, new File(baseDir));
for (int i = 0; i < classes.size(); i++) {
Class clazz = (Class) classes.get(i);
clearClass(clazz);
}
if (hasError) {
fail("Tomcat may clear the field above when reloading the web app");
}
}
private void check(ArrayList classes, File file) {
String name = file.getName();
if (file.isDirectory()) {
if (name.equals("CVS") || name.equals(".svn")) {
return;
}
File[] list = file.listFiles();
for (int i = 0; i < list.length; i++) {
check(classes, list[i]);
}
} 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 {
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
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);
}
}
}
protected void clearInstance(Object instance) throws Exception {
Field[] fields = instance.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
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;
}
clearField(instance.getClass().getName() + "." + field.getName() + " = " + o);
}
}
private void clearField(String s) {
for (int i = 0; i < KNOWN_REFRESHED.length; i++) {
if (s.startsWith(KNOWN_REFRESHED[i])) {
return;
}
}
hasError = true;
System.out.println(s);
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论