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

MVStore: reduced dependencies to other H2 classes.

上级 581f5126
......@@ -18,7 +18,10 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>There was a way to prevent a database from being re-opened,
<ul><li>Issue 565: MVStore: concurrently adding LOB objects
(with MULTI_THREADED option) resulted in a NullPointerException.
</li><li>MVStore: reduced dependencies to other H2 classes.
</li><li>There was a way to prevent a database from being re-opened,
by creating a column constraint that references a table with a higher id.
This is now detected, and creating the table is prohibited.
In future versions of H2, most likely creating references to other
......
......@@ -752,7 +752,7 @@ To build just the MVStore (without the database engine), run:
./build.sh jarMVStore
</pre>
<p>
This will create the file <code>bin/h2mvstore-${version}.jar</code> (about 130 KB).
This will create the file <code>bin/h2mvstore-${version}.jar</code> (about 200 KB).
</p>
<!-- [close] { --></div></td></tr></table><!-- } --><!-- analytics --></body></html>
......@@ -3,6 +3,7 @@ svn up
./build.sh spellcheck
./build.sh javadocImpl
./build.sh docs
./build.sh jarMVStore (should be about 200 KB)
Update Constants.java - change version and build number
Update changelog.html - add new version, remove oldest
Update newsfeed.sql - add new version, remove oldest
......
......@@ -14,7 +14,6 @@ import java.util.Properties;
import org.h2.engine.Constants;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.message.TraceSystem;
import org.h2.upgrade.DbUpgrade;
/*## Java 1.7 ##
......@@ -161,7 +160,7 @@ public class Driver implements java.sql.Driver {
DriverManager.registerDriver(INSTANCE);
}
} catch (SQLException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
return INSTANCE;
}
......@@ -176,7 +175,7 @@ public class Driver implements java.sql.Driver {
DriverManager.deregisterDriver(INSTANCE);
}
} catch (SQLException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
}
......
......@@ -56,6 +56,7 @@ import org.h2.table.TableView;
import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Server;
import org.h2.util.BitField;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.New;
......@@ -486,7 +487,7 @@ public class Database implements DataHandler {
traceSystem.close();
}
} catch (DbException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
}
Engine.getInstance().close(databaseName);
......@@ -1996,7 +1997,7 @@ public class Database implements DataHandler {
} else {
try {
eventListener = (DatabaseEventListener)
Utils.loadUserClass(className).newInstance();
JdbcUtils.loadUserClass(className).newInstance();
String url = databaseURL;
if (cipher != null) {
url += ";CIPHER=" + cipher;
......@@ -2726,7 +2727,7 @@ public class Database implements DataHandler {
!serializerName.equals("null")) {
try {
javaObjectSerializer = (JavaObjectSerializer)
Utils.loadUserClass(serializerName).newInstance();
JdbcUtils.loadUserClass(serializerName).newInstance();
} catch (Exception e) {
throw DbException.convert(e);
}
......
......@@ -13,6 +13,7 @@ import java.lang.reflect.Modifier;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import org.h2.Driver;
import org.h2.api.ErrorCode;
import org.h2.command.Parser;
......@@ -22,11 +23,11 @@ import org.h2.message.Trace;
import org.h2.schema.Schema;
import org.h2.schema.SchemaObjectBase;
import org.h2.table.Table;
import org.h2.util.JdbcUtils;
import org.h2.util.New;
import org.h2.util.SourceCompiler;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueArray;
......@@ -143,7 +144,7 @@ public class FunctionAlias extends SchemaObjectBase {
}
private void loadClass() {
Class<?> javaClass = Utils.loadUserClass(className);
Class<?> javaClass = JdbcUtils.loadUserClass(className);
Method[] methods = javaClass.getMethods();
ArrayList<JavaMethod> list = New.arrayList();
for (int i = 0, len = methods.length; i < len; i++) {
......
......@@ -9,6 +9,7 @@ package org.h2.engine;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode;
import org.h2.api.JavaObjectSerializer;
......@@ -25,13 +26,13 @@ import org.h2.store.FileStore;
import org.h2.store.LobStorageFrontend;
import org.h2.store.LobStorageInterface;
import org.h2.store.fs.FileUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.New;
import org.h2.util.SmallLRUCache;
import org.h2.util.StringUtils;
import org.h2.util.TempFileDeleter;
import org.h2.util.Utils;
import org.h2.value.Transfer;
import org.h2.value.Value;
......@@ -410,7 +411,7 @@ public class SessionRemote extends SessionWithState implements DataHandler {
if (className != null) {
className = StringUtils.trim(className, true, true, "'");
try {
eventListener = (DatabaseEventListener) Utils
eventListener = (DatabaseEventListener) JdbcUtils
.loadUserClass(className).newInstance();
} catch (Throwable e) {
throw DbException.convert(e);
......@@ -796,7 +797,7 @@ public class SessionRemote extends SessionWithState implements DataHandler {
serializerFQN = serializerFQN.trim();
if (!serializerFQN.isEmpty() && !serializerFQN.equals("null")) {
try {
javaObjectSerializer = (JavaObjectSerializer) Utils
javaObjectSerializer = (JavaObjectSerializer) JdbcUtils
.loadUserClass(serializerFQN).newInstance();
} catch (Exception e) {
throw DbException.convert(e);
......
......@@ -6,7 +6,6 @@
*/
package org.h2.engine;
import org.h2.message.TraceSystem;
import org.h2.util.MathUtils;
import org.h2.util.Utils;
......@@ -172,7 +171,7 @@ public class SysProperties {
* error.
*/
public static final int DATASOURCE_TRACE_LEVEL =
Utils.getProperty("h2.dataSourceTraceLevel", TraceSystem.ERROR);
Utils.getProperty("h2.dataSourceTraceLevel", 1);
/**
* System property <code>h2.delayWrongPasswordMin</code>
......
......@@ -6,18 +6,18 @@
*/
package org.h2.engine;
import org.h2.api.AggregateFunction;
import java.sql.Connection;
import java.sql.SQLException;
import org.h2.api.Aggregate;
import org.h2.api.AggregateFunction;
import org.h2.command.Parser;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.table.Table;
import org.h2.util.Utils;
import org.h2.util.JdbcUtils;
import org.h2.value.DataType;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Represents a user-defined aggregate function.
*/
......@@ -37,7 +37,7 @@ public class UserAggregate extends DbObjectBase {
public Aggregate getInstance() {
if (javaClass == null) {
javaClass = Utils.loadUserClass(className);
javaClass = JdbcUtils.loadUserClass(className);
}
Object obj;
try {
......
......@@ -40,6 +40,7 @@ import org.h2.message.DbException;
import org.h2.message.TraceObject;
import org.h2.result.ResultInterface;
import org.h2.util.CloseWatcher;
import org.h2.util.JdbcUtils;
import org.h2.util.Utils;
import org.h2.value.CompareMode;
import org.h2.value.Value;
......@@ -1908,7 +1909,7 @@ public class JdbcConnection extends TraceObject implements Connection {
}
case Value.JAVA_OBJECT:
if (SysProperties.serializeJavaObject) {
o = Utils.deserialize(v.getBytesNoCopy(), session.getDataHandler());
o = JdbcUtils.deserialize(v.getBytesNoCopy(), session.getDataHandler());
break;
}
default:
......
......@@ -8,7 +8,9 @@ package org.h2.message;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.Locale;
......@@ -63,9 +65,9 @@ public class DbException extends RuntimeException {
}
}
} catch (OutOfMemoryError e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
} catch (IOException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
}
......@@ -239,7 +241,7 @@ public class DbException extends RuntimeException {
*/
public static RuntimeException throwInternalError(String s) {
RuntimeException e = new RuntimeException(s);
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
throw e;
}
......@@ -371,4 +373,16 @@ public class DbException extends RuntimeException {
this.source = source;
}
/**
* Write the exception to the driver manager log writer if configured.
*
* @param e the exception
*/
public static void traceThrowable(Throwable e) {
PrintWriter writer = DriverManager.getLogWriter();
if (writer != null) {
e.printStackTrace(writer);
}
}
}
......@@ -371,7 +371,7 @@ public class TraceObject {
protected SQLException logAndConvert(Exception ex) {
SQLException e = DbException.toSQLException(ex);
if (trace == null) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
} else {
int errorCode = e.getErrorCode();
if (errorCode >= 23000 && errorCode < 24000) {
......
......@@ -10,9 +10,7 @@ import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.sql.DriverManager;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import org.h2.api.ErrorCode;
......@@ -119,18 +117,6 @@ public class TraceSystem implements TraceWriter {
this.sysOut = out;
}
/**
* Write the exception to the driver manager log writer if configured.
*
* @param e the exception
*/
public static void traceThrowable(Throwable e) {
PrintWriter writer = DriverManager.getLogWriter();
if (writer != null) {
e.printStackTrace(writer);
}
}
/**
* Get or create a trace object for this module. Trace modules with names
* such as "JDBC[1]" are not cached (modules where the name ends with "]").
......
......@@ -28,8 +28,8 @@ import org.h2.mvstore.db.MVTableEngine;
import org.h2.table.RegularTable;
import org.h2.table.Table;
import org.h2.table.TableLink;
import org.h2.util.JdbcUtils;
import org.h2.util.New;
import org.h2.util.Utils;
/**
* A schema as created by the SQL statement
......@@ -576,7 +576,7 @@ public class Schema extends DbObjectBase {
if (data.tableEngine != null) {
TableEngine engine;
try {
engine = (TableEngine) Utils.loadUserClass(data.tableEngine).newInstance();
engine = (TableEngine) JdbcUtils.loadUserClass(data.tableEngine).newInstance();
} catch (Exception e) {
throw DbException.convert(e);
}
......
......@@ -18,7 +18,7 @@ import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.result.Row;
import org.h2.table.Table;
import org.h2.util.Utils;
import org.h2.util.JdbcUtils;
import org.h2.util.StatementBuilder;
import org.h2.value.DataType;
import org.h2.value.Value;
......@@ -67,7 +67,7 @@ public class TriggerObject extends SchemaObjectBase {
try {
Session sysSession = database.getSystemSession();
Connection c2 = sysSession.createConnection(false);
Object obj = Utils.loadUserClass(triggerClassName).newInstance();
Object obj = JdbcUtils.loadUserClass(triggerClassName).newInstance();
triggerCallback = (Trigger) obj;
triggerCallback.init(c2, getSchema().getName(), getName(),
table.getName(), before, typeMask);
......
......@@ -25,7 +25,6 @@ import org.h2.Driver;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.message.DbException;
import org.h2.message.TraceSystem;
import org.h2.util.JdbcUtils;
import org.h2.util.NetUtils;
import org.h2.util.New;
......@@ -139,7 +138,7 @@ public class TcpServer implements Service {
managementDbAdd.setString(3, user);
managementDbAdd.execute();
} catch (SQLException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
}
......@@ -153,7 +152,7 @@ public class TcpServer implements Service {
managementDbRemove.setInt(1, id);
managementDbRemove.execute();
} catch (SQLException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
}
......@@ -162,7 +161,7 @@ public class TcpServer implements Service {
try {
managementDb.close();
} catch (SQLException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
managementDb = null;
}
......@@ -260,7 +259,7 @@ public class TcpServer implements Service {
serverSocket = NetUtils.closeSilently(serverSocket);
} catch (Exception e) {
if (!stop) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
}
stopManagementDb();
......@@ -296,7 +295,7 @@ public class TcpServer implements Service {
try {
serverSocket.close();
} catch (IOException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
} catch (NullPointerException e) {
// ignore
}
......@@ -306,7 +305,7 @@ public class TcpServer implements Service {
try {
listenerThread.join(1000);
} catch (InterruptedException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
}
}
......@@ -317,7 +316,7 @@ public class TcpServer implements Service {
try {
c.getThread().join(100);
} catch (Exception e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
}
}
......
......@@ -18,7 +18,6 @@ import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
......@@ -30,7 +29,7 @@ import java.util.TimeZone;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.message.TraceSystem;
import org.h2.message.DbException;
import org.h2.server.Service;
import org.h2.server.ShutdownHandler;
import org.h2.store.fs.FileUtils;
......@@ -406,7 +405,7 @@ public class WebServer implements Service {
try {
listenerThread.join(1000);
} catch (InterruptedException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
}
// TODO server: using a boolean 'now' argument? a timeout?
......@@ -477,7 +476,7 @@ public class WebServer implements Service {
}
}
} catch (IOException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
session.put("text", new HashMap<Object, Object>(text));
}
......@@ -563,7 +562,7 @@ public class WebServer implements Service {
return SortedProperties.loadProperties(
serverPropertiesDir + "/" + Constants.SERVER_PROPERTIES_NAME);
} catch (Exception e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
return new Properties();
}
}
......@@ -650,7 +649,7 @@ public class WebServer implements Service {
out.close();
}
} catch (Exception e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
}
......
......@@ -19,7 +19,7 @@ import java.util.Locale;
import org.h2.bnf.Bnf;
import org.h2.bnf.context.DbContents;
import org.h2.bnf.context.DbContextRule;
import org.h2.message.TraceSystem;
import org.h2.message.DbException;
import org.h2.util.New;
/**
......@@ -203,7 +203,7 @@ class WebSession {
m.put("executing", executingStatement == null ?
"${text.admin.no}" : "${text.admin.yes}");
} catch (SQLException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
return m;
}
......
......@@ -22,7 +22,7 @@ import java.util.Properties;
import java.util.StringTokenizer;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.message.TraceSystem;
import org.h2.message.DbException;
import org.h2.mvstore.DataUtils;
import org.h2.util.IOUtils;
import org.h2.util.NetUtils;
......@@ -96,7 +96,7 @@ class WebThread extends WebApp implements Runnable {
}
}
} catch (Exception e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
IOUtils.closeSilently(output);
IOUtils.closeSilently(input);
......
......@@ -27,7 +27,6 @@ import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.util.IOUtils;
import org.h2.util.New;
import org.h2.util.Utils;
/**
* This file system stores files on disk.
......@@ -362,7 +361,7 @@ public class FilePathDisk extends FilePath {
String prefix = new File(fileName).getName();
File dir;
if (inTempDir) {
dir = new File(Utils.getProperty("java.io.tmpdir", "."));
dir = new File(System.getProperty("java.io.tmpdir", "."));
} else {
dir = new File(fileName).getAbsoluteFile().getParentFile();
}
......
......@@ -17,11 +17,11 @@ import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.store.fs.FileUtils;
import org.h2.util.Utils;
import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.ScriptReader;
......@@ -116,7 +116,7 @@ public class RunScript extends Tool {
showTime = true;
} else if (arg.equals("-driver")) {
String driver = args[++i];
Utils.loadUserClass(driver);
JdbcUtils.loadUserClass(driver);
} else if (arg.equals("-options")) {
StringBuilder buff = new StringBuilder();
i++;
......
......@@ -13,7 +13,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode;
import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.message.TraceSystem;
import org.h2.server.Service;
import org.h2.server.ShutdownHandler;
import org.h2.server.TcpServer;
......@@ -569,7 +568,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
try {
service.listen();
} catch (Exception e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
}
}
......
......@@ -128,7 +128,7 @@ public class Shell extends Tool implements Runnable {
password = args[++i];
} else if (arg.equals("-driver")) {
String driver = args[++i];
Utils.loadUserClass(driver);
JdbcUtils.loadUserClass(driver);
} else if (arg.equals("-sql")) {
sql = args[++i];
} else if (arg.equals("-properties")) {
......
......@@ -32,9 +32,9 @@ import java.util.Map;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.Utils;
import org.h2.value.DataType;
/**
......@@ -528,7 +528,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData {
if (o == null || o instanceof byte[]) {
return (byte[]) o;
}
return Utils.serialize(o, null);
return JdbcUtils.serialize(o, null);
}
/**
......
......@@ -29,7 +29,7 @@ public class DbDriverActivator implements BundleActivator {
public void start(BundleContext bundleContext) {
org.h2.Driver driver = org.h2.Driver.load();
try {
Utils.loadUserClass(DATASOURCE_FACTORY_CLASS);
JdbcUtils.loadUserClass(DATASOURCE_FACTORY_CLASS);
} catch (Exception e) {
// class not found - don't register
return;
......
......@@ -6,21 +6,41 @@
*/
package org.h2.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Properties;
import javax.naming.Context;
import javax.sql.DataSource;
import org.h2.api.ErrorCode;
import org.h2.api.JavaObjectSerializer;
import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.store.DataHandler;
import org.h2.util.Utils.ClassFactory;
/**
* This is a utility class with JDBC helper functions.
*/
public class JdbcUtils {
/**
* The serializer to use.
*/
public static JavaObjectSerializer serializer;
private static final String[] DRIVERS = {
"h2:", "org.h2.Driver",
"Cache:", "com.intersys.jdbc.CacheDriver",
......@@ -48,11 +68,137 @@ public class JdbcUtils {
"sqlserver:", "com.microsoft.sqlserver.jdbc.SQLServerDriver",
"teradata:", "com.ncr.teradata.TeraDriver",
};
private static boolean allowAllClasses;
private static HashSet<String> allowedClassNames;
/**
* In order to manage more than one class loader
*/
private static ArrayList<ClassFactory> userClassFactories =
new ArrayList<ClassFactory>();
private static String[] allowedClassNamePrefixes;
private JdbcUtils() {
// utility class
}
/**
* Add a class factory in order to manage more than one class loader.
*
* @param classFactory An object that implements ClassFactory
*/
public static void addClassFactory(ClassFactory classFactory) {
getUserClassFactories().add(classFactory);
}
/**
* Remove a class factory
*
* @param classFactory Already inserted class factory instance
*/
public static void removeClassFactory(ClassFactory classFactory) {
getUserClassFactories().remove(classFactory);
}
private static ArrayList<ClassFactory> getUserClassFactories() {
if (userClassFactories == null) {
// initially, it is empty
// but Apache Tomcat may clear the fields as well
userClassFactories = new ArrayList<ClassFactory>();
}
return userClassFactories;
}
static {
String clazz = SysProperties.JAVA_OBJECT_SERIALIZER;
if (clazz != null) {
try {
serializer = (JavaObjectSerializer) loadUserClass(clazz).newInstance();
} catch (Exception e) {
throw DbException.convert(e);
}
}
}
/**
* Load a class, but check if it is allowed to load this class first. To
* perform access rights checking, the system property h2.allowedClasses
* needs to be set to a list of class file name prefixes.
*
* @param className the name of the class
* @return the class object
*/
public static Class<?> loadUserClass(String className) {
if (allowedClassNames == null) {
// initialize the static fields
String s = SysProperties.ALLOWED_CLASSES;
ArrayList<String> prefixes = New.arrayList();
boolean allowAll = false;
HashSet<String> classNames = New.hashSet();
for (String p : StringUtils.arraySplit(s, ',', true)) {
if (p.equals("*")) {
allowAll = true;
} else if (p.endsWith("*")) {
prefixes.add(p.substring(0, p.length() - 1));
} else {
classNames.add(p);
}
}
allowedClassNamePrefixes = new String[prefixes.size()];
prefixes.toArray(allowedClassNamePrefixes);
allowAllClasses = allowAll;
allowedClassNames = classNames;
}
if (!allowAllClasses && !allowedClassNames.contains(className)) {
boolean allowed = false;
for (String s : allowedClassNamePrefixes) {
if (className.startsWith(s)) {
allowed = true;
}
}
if (!allowed) {
throw DbException.get(
ErrorCode.ACCESS_DENIED_TO_CLASS_1, className);
}
}
// Use provided class factory first.
for (ClassFactory classFactory : getUserClassFactories()) {
if (classFactory.match(className)) {
try {
Class<?> userClass = classFactory.loadClass(className);
if (!(userClass == null)) {
return userClass;
}
} catch (Exception e) {
throw DbException.get(
ErrorCode.CLASS_NOT_FOUND_1, e, className);
}
}
}
// Use local ClassLoader
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
try {
return Class.forName(
className, true,
Thread.currentThread().getContextClassLoader());
} catch (Exception e2) {
throw DbException.get(
ErrorCode.CLASS_NOT_FOUND_1, e, className);
}
} catch (NoClassDefFoundError e) {
throw DbException.get(
ErrorCode.CLASS_NOT_FOUND_1, e, className);
} catch (Error e) {
// UnsupportedClassVersionError
throw DbException.get(
ErrorCode.GENERAL_ERROR_1, e, className);
}
}
/**
* Close a statement without throwing an exception.
*
......@@ -132,7 +278,7 @@ public class JdbcUtils {
if (StringUtils.isNullOrEmpty(driver)) {
JdbcUtils.load(url);
} else {
Class<?> d = Utils.loadUserClass(driver);
Class<?> d = loadUserClass(driver);
if (java.sql.Driver.class.isAssignableFrom(d)) {
return DriverManager.getConnection(url, prop);
} else if (javax.naming.Context.class.isAssignableFrom(d)) {
......@@ -185,7 +331,81 @@ public class JdbcUtils {
public static void load(String url) {
String driver = getDriver(url);
if (driver != null) {
Utils.loadUserClass(driver);
loadUserClass(driver);
}
}
/**
* Serialize the object to a byte array, using the serializer specified by
* the connection info if set, or the default serializer.
*
* @param obj the object to serialize
* @param dataHandler provides the object serializer (may be null)
* @return the byte array
*/
public static byte[] serialize(Object obj, DataHandler dataHandler) {
try {
JavaObjectSerializer handlerSerializer = null;
if (dataHandler != null) {
handlerSerializer = dataHandler.getJavaObjectSerializer();
}
if (handlerSerializer != null) {
return handlerSerializer.serialize(obj);
}
if (serializer != null) {
return serializer.serialize(obj);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(out);
os.writeObject(obj);
return out.toByteArray();
} catch (Throwable e) {
throw DbException.get(ErrorCode.SERIALIZATION_FAILED_1, e, e.toString());
}
}
/**
* De-serialize the byte array to an object, eventually using the serializer
* specified by the connection info.
*
* @param data the byte array
* @param dataHandler provides the object serializer (may be null)
* @return the object
* @throws DbException if serialization fails
*/
public static Object deserialize(byte[] data, DataHandler dataHandler) {
try {
JavaObjectSerializer dbJavaObjectSerializer = null;
if (dataHandler != null) {
dbJavaObjectSerializer = dataHandler.getJavaObjectSerializer();
}
if (dbJavaObjectSerializer != null) {
return dbJavaObjectSerializer.deserialize(data);
}
if (serializer != null) {
return serializer.deserialize(data);
}
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream is;
if (SysProperties.USE_THREAD_CONTEXT_CLASS_LOADER) {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
is = new ObjectInputStream(in) {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
try {
return Class.forName(desc.getName(), true, loader);
} catch (ClassNotFoundException e) {
return super.resolveClass(desc);
}
}
};
} else {
is = new ObjectInputStream(in);
}
return is.readObject();
} catch (Throwable e) {
throw DbException.get(ErrorCode.DESERIALIZATION_FAILED_1, e, e.toString());
}
}
......
......@@ -22,8 +22,6 @@ import java.util.Properties;
import java.util.TreeMap;
import java.util.Vector;
import java.util.Map.Entry;
import org.h2.message.DbException;
import org.h2.message.TraceSystem;
import org.h2.store.fs.FileUtils;
/**
......@@ -58,7 +56,7 @@ public class SortedProperties extends Properties {
try {
return Boolean.parseBoolean(value);
} catch (Exception e) {
TraceSystem.traceThrowable(e);
e.printStackTrace();
return def;
}
}
......@@ -76,7 +74,7 @@ public class SortedProperties extends Properties {
try {
return Integer.decode(value);
} catch (Exception e) {
TraceSystem.traceThrowable(e);
e.printStackTrace();
return def;
}
}
......@@ -119,7 +117,7 @@ public class SortedProperties extends Properties {
try {
w = new OutputStreamWriter(FileUtils.newOutputStream(fileName, false));
} catch (Exception e) {
throw DbException.convertToIOException(e);
throw new IOException(e.toString(), e);
}
PrintWriter writer = new PrintWriter(new BufferedWriter(w));
while (true) {
......
......@@ -6,11 +6,15 @@
*/
package org.h2.util;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A method call that is executed in a separate thread. If the method throws an
* exception, it is wrapped in a RuntimeException.
*/
public abstract class Task implements Runnable {
private static AtomicInteger counter = new AtomicInteger();
/**
* A flag indicating the get() method has been called.
......@@ -51,7 +55,7 @@ public abstract class Task implements Runnable {
* @return this
*/
public Task execute() {
return execute(getClass().getName());
return execute(getClass().getName() + ":" + counter.getAndIncrement());
}
/**
......
......@@ -6,40 +6,23 @@
*/
package org.h2.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.h2.api.ErrorCode;
import org.h2.api.JavaObjectSerializer;
import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.store.DataHandler;
/**
* This utility class contains miscellaneous functions.
*/
public class Utils {
/**
* The serializer to use.
*/
public static JavaObjectSerializer serializer;
/**
* An 0-size byte array.
*/
......@@ -61,59 +44,10 @@ public class Utils {
private static final HashMap<String, byte[]> RESOURCES = New.hashMap();
private static boolean allowAllClasses;
private static HashSet<String> allowedClassNames;
/**
* In order to manage more than one class loader
*/
private static ArrayList<ClassFactory> userClassFactories =
new ArrayList<ClassFactory>();
private static String[] allowedClassNamePrefixes;
static {
String clazz = SysProperties.JAVA_OBJECT_SERIALIZER;
if (clazz != null) {
try {
serializer = (JavaObjectSerializer) loadUserClass(clazz).newInstance();
} catch (Exception e) {
throw DbException.convert(e);
}
}
}
private Utils() {
// utility class
}
/**
* Add a class factory in order to manage more than one class loader.
*
* @param classFactory An object that implements ClassFactory
*/
public static void addClassFactory(ClassFactory classFactory) {
getUserClassFactories().add(classFactory);
}
/**
* Remove a class factory
*
* @param classFactory Already inserted class factory instance
*/
public static void removeClassFactory(ClassFactory classFactory) {
getUserClassFactories().remove(classFactory);
}
private static ArrayList<ClassFactory> getUserClassFactories() {
if (userClassFactories == null) {
// initially, it is empty
// but Apache Tomcat may clear the fields as well
userClassFactories = new ArrayList<ClassFactory>();
}
return userClassFactories;
}
private static int readInt(byte[] buff, int pos) {
return (buff[pos++] << 24) +
((buff[pos++] & 0xff) << 16) +
......@@ -332,94 +266,6 @@ public class Utils {
return copy;
}
/**
* Serialize the object to a byte array, using the serializer specified by
* the connection info if set, or the default serializer.
*
* @param obj the object to serialize
* @param dataHandler provides the object serializer (may be null)
* @return the byte array
*/
public static byte[] serialize(Object obj, DataHandler dataHandler) {
try {
JavaObjectSerializer handlerSerializer = null;
if (dataHandler != null) {
handlerSerializer = dataHandler.getJavaObjectSerializer();
}
if (handlerSerializer != null) {
return handlerSerializer.serialize(obj);
}
if (serializer != null) {
return serializer.serialize(obj);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(out);
os.writeObject(obj);
return out.toByteArray();
} catch (Throwable e) {
throw DbException.get(ErrorCode.SERIALIZATION_FAILED_1, e, e.toString());
}
}
/**
* De-serialize the byte array to an object.
*
* @param data the byte array
* @return the object
* @throws DbException if serialization fails
*
* @deprecated use {@link #deserialize(byte[], DataHandler)} instead
*/
@Deprecated
public static Object deserialize(byte[] data) {
return deserialize(data, null);
}
/**
* De-serialize the byte array to an object, eventually using the serializer
* specified by the connection info.
*
* @param data the byte array
* @param dataHandler provides the object serializer (may be null)
* @return the object
* @throws DbException if serialization fails
*/
public static Object deserialize(byte[] data, DataHandler dataHandler) {
try {
JavaObjectSerializer dbJavaObjectSerializer = null;
if (dataHandler != null) {
dbJavaObjectSerializer = dataHandler.getJavaObjectSerializer();
}
if (dbJavaObjectSerializer != null) {
return dbJavaObjectSerializer.deserialize(data);
}
if (serializer != null) {
return serializer.deserialize(data);
}
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream is;
if (SysProperties.USE_THREAD_CONTEXT_CLASS_LOADER) {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
is = new ObjectInputStream(in) {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
try {
return Class.forName(desc.getName(), true, loader);
} catch (ClassNotFoundException e) {
return super.resolveClass(desc);
}
}
};
} else {
is = new ObjectInputStream(in);
}
return is.readObject();
} catch (Throwable e) {
throw DbException.get(ErrorCode.DESERIALIZATION_FAILED_1, e, e.toString());
}
}
/**
* Calculate the hash code of the given object. The object may be null.
*
......@@ -605,83 +451,6 @@ public class Utils {
return top1 == top2;
}
/**
* Load a class, but check if it is allowed to load this class first. To
* perform access rights checking, the system property h2.allowedClasses
* needs to be set to a list of class file name prefixes.
*
* @param className the name of the class
* @return the class object
*/
public static Class<?> loadUserClass(String className) {
if (allowedClassNames == null) {
// initialize the static fields
String s = SysProperties.ALLOWED_CLASSES;
ArrayList<String> prefixes = New.arrayList();
boolean allowAll = false;
HashSet<String> classNames = New.hashSet();
for (String p : StringUtils.arraySplit(s, ',', true)) {
if (p.equals("*")) {
allowAll = true;
} else if (p.endsWith("*")) {
prefixes.add(p.substring(0, p.length() - 1));
} else {
classNames.add(p);
}
}
allowedClassNamePrefixes = new String[prefixes.size()];
prefixes.toArray(allowedClassNamePrefixes);
allowAllClasses = allowAll;
allowedClassNames = classNames;
}
if (!allowAllClasses && !allowedClassNames.contains(className)) {
boolean allowed = false;
for (String s : allowedClassNamePrefixes) {
if (className.startsWith(s)) {
allowed = true;
}
}
if (!allowed) {
throw DbException.get(
ErrorCode.ACCESS_DENIED_TO_CLASS_1, className);
}
}
// Use provided class factory first.
for (ClassFactory classFactory : getUserClassFactories()) {
if (classFactory.match(className)) {
try {
Class<?> userClass = classFactory.loadClass(className);
if (!(userClass == null)) {
return userClass;
}
} catch (Exception e) {
throw DbException.get(
ErrorCode.CLASS_NOT_FOUND_1, e, className);
}
}
}
// Use local ClassLoader
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
try {
return Class.forName(
className, true,
Thread.currentThread().getContextClassLoader());
} catch (Exception e2) {
throw DbException.get(
ErrorCode.CLASS_NOT_FOUND_1, e, className);
}
} catch (NoClassDefFoundError e) {
throw DbException.get(
ErrorCode.CLASS_NOT_FOUND_1, e, className);
} catch (Error e) {
// UnsupportedClassVersionError
throw DbException.get(
ErrorCode.GENERAL_ERROR_1, e, className);
}
}
/**
* Get a resource from the resource map.
*
......
......@@ -9,9 +9,10 @@ package org.h2.value;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.Locale;
import org.h2.message.DbException;
import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
/**
* An implementation of CompareMode that uses the ICU4J Collator.
......@@ -45,7 +46,7 @@ public class CompareModeIcu4J extends CompareMode {
private static Comparator<String> getIcu4jCollator(String name, int strength) {
try {
Comparator<String> result = null;
Class<?> collatorClass = Utils.loadUserClass(
Class<?> collatorClass = JdbcUtils.loadUserClass(
"com.ibm.icu.text.Collator");
Method getInstanceMethod = collatorClass.getMethod(
"getInstance", Locale.class);
......
......@@ -33,6 +33,7 @@ import org.h2.jdbc.JdbcClob;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.tools.SimpleResultSet;
import org.h2.util.JdbcUtils;
import org.h2.util.New;
import org.h2.util.Utils;
......@@ -175,7 +176,7 @@ public class DataType {
static {
Class<?> g;
try {
g = Utils.loadUserClass(GEOMETRY_CLASS_NAME);
g = JdbcUtils.loadUserClass(GEOMETRY_CLASS_NAME);
} catch (Exception e) {
// class is not in the classpath - ignore
g = null;
......@@ -1194,7 +1195,7 @@ public class DataType {
return new JdbcClob(conn, v, 0);
}
if (v.getType() == Value.JAVA_OBJECT) {
Object o = SysProperties.serializeJavaObject ? Utils.deserialize(v.getBytes(),
Object o = SysProperties.serializeJavaObject ? JdbcUtils.deserialize(v.getBytes(),
conn.getSession().getDataHandler()) : v.getObject();
if (paramClass.isAssignableFrom(o.getClass())) {
return o;
......
......@@ -24,7 +24,6 @@ import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
import org.h2.message.DbException;
import org.h2.message.TraceSystem;
import org.h2.mvstore.DataUtils;
import org.h2.security.SHA256;
import org.h2.store.Data;
......@@ -32,6 +31,7 @@ import org.h2.store.DataReader;
import org.h2.tools.SimpleResultSet;
import org.h2.util.DateTimeUtils;
import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.StringUtils;
......@@ -321,7 +321,7 @@ public class Transfer {
socket.close();
}
} catch (IOException e) {
TraceSystem.traceThrowable(e);
DbException.traceThrowable(e);
} finally {
socket = null;
}
......@@ -665,7 +665,7 @@ public class Transfer {
Class<?> componentType = Object.class;
if (len < 0) {
len = -(len + 1);
componentType = Utils.loadUserClass(readString());
componentType = JdbcUtils.loadUserClass(readString());
}
Value[] list = new Value[len];
for (int i = 0; i < len; i++) {
......
......@@ -27,6 +27,7 @@ import org.h2.message.DbException;
import org.h2.store.DataHandler;
import org.h2.tools.SimpleResultSet;
import org.h2.util.DateTimeUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
......@@ -817,7 +818,7 @@ public abstract class Value {
case BYTES:
return ValueGeometry.get(getBytesNoCopy());
case JAVA_OBJECT:
Object object = Utils.deserialize(getBytesNoCopy(), getDataHandler());
Object object = JdbcUtils.deserialize(getBytesNoCopy(), getDataHandler());
if (DataType.isGeometry(object)) {
return ValueGeometry.getFromGeometry(object);
}
......
......@@ -12,6 +12,7 @@ import java.sql.Types;
import org.h2.engine.SysProperties;
import org.h2.store.DataHandler;
import org.h2.util.JdbcUtils;
import org.h2.util.Utils;
/**
......@@ -45,7 +46,7 @@ public class ValueJavaObject extends ValueBytes {
ValueJavaObject obj;
if (SysProperties.serializeJavaObject) {
if (b == null) {
b = Utils.serialize(javaObject, dataHandler);
b = JdbcUtils.serialize(javaObject, dataHandler);
}
obj = new ValueJavaObject(b, dataHandler);
} else {
......@@ -65,7 +66,7 @@ public class ValueJavaObject extends ValueBytes {
@Override
public void set(PreparedStatement prep, int parameterIndex)
throws SQLException {
Object obj = Utils.deserialize(getBytesNoCopy(), getDataHandler());
Object obj = JdbcUtils.deserialize(getBytesNoCopy(), getDataHandler());
prep.setObject(parameterIndex, obj, Types.JAVA_OBJECT);
}
......@@ -95,7 +96,7 @@ public class ValueJavaObject extends ValueBytes {
@Override
public byte[] getBytesNoCopy() {
if (value == null) {
value = Utils.serialize(javaObject, null);
value = JdbcUtils.serialize(javaObject, null);
}
return value;
}
......@@ -163,7 +164,7 @@ public class ValueJavaObject extends ValueBytes {
@Override
public Object getObject() {
if (javaObject == null) {
javaObject = Utils.deserialize(value, getDataHandler());
javaObject = JdbcUtils.deserialize(value, getDataHandler());
}
return javaObject;
}
......
......@@ -36,7 +36,6 @@ import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils;
import org.h2.util.Task;
import org.h2.util.Utils;
/**
* Tests LOB and CLOB data types.
......@@ -1430,7 +1429,7 @@ public class TestLob extends TestBase {
conn.createStatement().execute("drop table test");
stat.execute("create table test(value other)");
prep = conn.prepareStatement("insert into test values(?)");
prep.setObject(1, Utils.serialize("", conn.getSession().getDataHandler()));
prep.setObject(1, JdbcUtils.serialize("", conn.getSession().getDataHandler()));
prep.execute();
rs = stat.executeQuery("select value from test");
while (rs.next()) {
......
......@@ -27,6 +27,7 @@ import org.h2.api.ErrorCode;
import org.h2.test.TestBase;
import org.h2.tools.SimpleResultSet;
import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.Utils;
/**
......@@ -375,7 +376,7 @@ public class TestCallableStatement extends TestBase {
private void testClassLoader(Connection conn) throws SQLException {
Utils.ClassFactory myFactory = new TestClassFactory();
Utils.addClassFactory(myFactory);
JdbcUtils.addClassFactory(myFactory);
try {
Statement stat = conn.createStatement();
stat.execute("CREATE ALIAS T_CLASSLOADER FOR \"TestClassFactory.testClassF\"");
......@@ -383,7 +384,7 @@ public class TestCallableStatement extends TestBase {
assertTrue(rs.next());
assertEquals(false, rs.getBoolean(1));
} finally {
Utils.removeClassFactory(myFactory);
JdbcUtils.removeClassFactory(myFactory);
}
}
......
......@@ -13,7 +13,7 @@ import java.sql.Statement;
import java.sql.Types;
import org.h2.api.JavaObjectSerializer;
import org.h2.test.TestBase;
import org.h2.util.Utils;
import org.h2.util.JdbcUtils;
/**
* Tests {@link JavaObjectSerializer}.
......@@ -47,7 +47,7 @@ public class TestJavaObjectSerializer extends TestBase {
}
private void testStaticGlobalSerializer() throws Exception {
Utils.serializer = new JavaObjectSerializer() {
JdbcUtils.serializer = new JavaObjectSerializer() {
@Override
public byte[] serialize(Object obj) throws Exception {
assertEquals(100500, ((Integer) obj).intValue());
......@@ -86,7 +86,7 @@ public class TestJavaObjectSerializer extends TestBase {
conn.close();
deleteDb("javaSerializer");
} finally {
Utils.serializer = null;
JdbcUtils.serializer = null;
}
}
......
......@@ -8,8 +8,8 @@ package org.h2.test.unit;
import org.h2.message.DbException;
import org.h2.test.TestBase;
import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
/**
* Tests the ability to deserialize objects that are not part of the system
......@@ -46,7 +46,7 @@ public class TestObjectDeserialization extends TestBase {
usesThreadContextClassLoader = false;
Thread.currentThread().setContextClassLoader(new TestClassLoader());
try {
Utils.deserialize(StringUtils.convertHexToBytes(OBJECT), null);
JdbcUtils.deserialize(StringUtils.convertHexToBytes(OBJECT), null);
fail();
} catch (DbException e) {
// expected
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论