提交 7020bad7 authored 作者: Thomas Mueller's avatar Thomas Mueller

Remove unused code.

上级 0ec9128a
......@@ -21,7 +21,6 @@ import org.h2.server.web.DbContextRule;
import org.h2.tools.Csv;
import org.h2.util.New;
import org.h2.util.Resources;
import org.h2.util.StringCache;
import org.h2.util.StringUtils;
/**
......@@ -323,7 +322,7 @@ public class Bnf {
while (tokenizer.hasMoreTokens()) {
String s = tokenizer.nextToken();
// avoid duplicate strings
s = StringCache.get(s);
s = StringUtils.cache(s);
if (s.length() == 1) {
if (" \r\n".indexOf(s.charAt(0)) >= 0) {
continue;
......
......@@ -131,7 +131,6 @@ import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.ObjectArray;
import org.h2.util.StatementBuilder;
import org.h2.util.StringCache;
import org.h2.util.StringUtils;
import org.h2.value.CompareMode;
import org.h2.value.DataType;
......@@ -2616,7 +2615,7 @@ public class Parser {
}
i++;
}
currentToken = StringCache.getNew(sqlCommand.substring(start, i));
currentToken = StringUtils.fromCacheOrNew(sqlCommand.substring(start, i));
currentTokenType = getTokenType(currentToken);
parseIndex = i;
return;
......@@ -2638,7 +2637,7 @@ public class Parser {
}
i++;
}
currentToken = StringCache.getNew(result);
currentToken = StringUtils.fromCacheOrNew(result);
parseIndex = i;
currentTokenQuoted = true;
currentTokenType = IDENTIFIER;
......@@ -2737,7 +2736,7 @@ public class Parser {
}
currentToken = "'";
checkLiterals(true);
currentValue = ValueString.get(StringCache.getNew(result));
currentValue = ValueString.get(StringUtils.fromCacheOrNew(result));
parseIndex = i;
currentTokenType = VALUE;
return;
......@@ -2751,7 +2750,7 @@ public class Parser {
result = sqlCommand.substring(begin, i);
currentToken = "'";
checkLiterals(true);
currentValue = ValueString.get(StringCache.getNew(result));
currentValue = ValueString.get(StringUtils.fromCacheOrNew(result));
parseIndex = i;
currentTokenType = VALUE;
return;
......@@ -3873,7 +3872,7 @@ public class Parser {
data.session = session;
recursiveTable = schema.createTable(data);
session.addLocalTempTable(recursiveTable);
String querySQL = StringCache.getNew(sqlCommand.substring(parseIndex));
String querySQL = StringUtils.fromCacheOrNew(sqlCommand.substring(parseIndex));
read("AS");
Query withQuery = parseSelect();
withQuery.prepare();
......@@ -3902,7 +3901,7 @@ public class Parser {
String[] cols = parseColumnList();
command.setColumnNames(cols);
}
String select = StringCache.getNew(sqlCommand.substring(parseIndex));
String select = StringUtils.fromCacheOrNew(sqlCommand.substring(parseIndex));
read("AS");
try {
Query query = parseSelect();
......
......@@ -20,7 +20,6 @@ import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.log.LogSystem;
import org.h2.message.Message;
import org.h2.result.ResultInterface;
import org.h2.store.FileLister;
......@@ -61,8 +60,7 @@ public class BackupCommand extends Prepared {
name = FileUtils.getFileName(name);
OutputStream zip = FileUtils.openFileOutputStream(fileName, false);
ZipOutputStream out = new ZipOutputStream(zip);
LogSystem log = db.getLog();
log.flush();
db.flush();
String fn;
fn = db.getName() + Constants.SUFFIX_PAGE_FILE;
backupPageStore(out, fn, db.getPageStore());
......
......@@ -11,8 +11,8 @@ import org.h2.api.Trigger;
import org.h2.command.Prepared;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord;
import org.h2.expression.Expression;
import org.h2.log.UndoLogRecord;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
import org.h2.result.RowList;
......
......@@ -14,10 +14,10 @@ import org.h2.constant.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord;
import org.h2.expression.Expression;
import org.h2.expression.Parameter;
import org.h2.index.PageIndex;
import org.h2.log.UndoLogRecord;
import org.h2.message.Message;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
......
......@@ -13,10 +13,10 @@ import org.h2.command.Prepared;
import org.h2.constant.ErrorCode;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord;
import org.h2.expression.Expression;
import org.h2.expression.Parameter;
import org.h2.index.Index;
import org.h2.log.UndoLogRecord;
import org.h2.message.Message;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
......
......@@ -532,7 +532,7 @@ public class ScriptCommand extends ScriptBase {
s += ";";
if (out != null) {
byte[] buff = StringUtils.utf8Encode(s);
int len = MathUtils.roundUp(buff.length + lineSeparator.length, Constants.FILE_BLOCK_SIZE);
int len = MathUtils.roundUpInt(buff.length + lineSeparator.length, Constants.FILE_BLOCK_SIZE);
buffer = ByteUtils.copy(buff, buffer);
if (len > buffer.length) {
......
......@@ -19,7 +19,6 @@ import org.h2.message.Message;
import org.h2.security.SHA256;
import org.h2.util.ByteUtils;
import org.h2.util.FileUtils;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StringUtils;
......@@ -444,7 +443,7 @@ public class ConnectionInfo implements Cloneable {
String key = SetTypes.getTypeName(setting);
String s = getProperty(key, null);
try {
return s == null ? defaultValue : MathUtils.decodeInt(s);
return s == null ? defaultValue : Integer.decode(s);
} catch (NumberFormatException e) {
return defaultValue;
}
......
......@@ -24,8 +24,6 @@ import org.h2.constraint.Constraint;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.log.LogSystem;
import org.h2.log.UndoLogRecord;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
......@@ -38,6 +36,7 @@ import org.h2.schema.TriggerObject;
import org.h2.store.DataHandler;
import org.h2.store.FileLock;
import org.h2.store.FileStore;
import org.h2.store.InDoubtTransaction;
import org.h2.store.PageStore;
import org.h2.store.WriterThread;
import org.h2.store.fs.FileSystemMemory;
......@@ -115,7 +114,6 @@ public class Database implements DataHandler {
private TableData meta;
private Index metaIdIndex;
private FileLock lock;
private LogSystem log;
private WriterThread writer;
private boolean starting;
private TraceSystem traceSystem;
......@@ -167,6 +165,7 @@ public class Database implements DataHandler {
private boolean compactFully;
private SourceCompiler compiler;
private boolean metaTablesInitialized;
private boolean flushOnEachCommit;
public Database(String name, ConnectionInfo ci, String cipher) throws SQLException {
this.compareMode = CompareMode.getInstance(null, 0);
......@@ -317,7 +316,7 @@ public class Database implements DataHandler {
long now = System.currentTimeMillis();
if (now > reconnectCheckNext) {
if (pending) {
String pos = log == null ? null : log.getWritePos();
String pos = pageStore == null ? null : "" + pageStore.getWriteCountTotal();
lock.setProperty("logPos", pos);
lock.save();
}
......@@ -338,7 +337,7 @@ public class Database implements DataHandler {
return false;
}
}
String pos = log == null ? null : log.getWritePos();
String pos = pageStore == null ? null : "" + pageStore.getWriteCountTotal();
lock.setProperty("logPos", pos);
if (pending) {
lock.setProperty("changePending", "true-" + Math.random());
......@@ -400,10 +399,7 @@ public class Database implements DataHandler {
if (powerOffCount != -1) {
try {
powerOffCount = -1;
if (log != null) {
stopWriter();
log = null;
}
stopWriter();
if (pageStore != null) {
try {
pageStore.close();
......@@ -543,12 +539,10 @@ public class Database implements DataHandler {
starting = true;
getPageStore();
starting = false;
log = new LogSystem(this, readOnly, pageStore);
reserveLobFileObjectIds();
writer = WriterThread.create(this, writeDelay);
} else {
traceSystem = new TraceSystem(null);
log = new LogSystem(null, false, null);
}
systemUser = new User(this, 0, SYSTEM_USER_NAME, true);
mainSchema = new Schema(this, 0, Constants.SCHEMA_MAIN, systemUser, true);
......@@ -1112,10 +1106,7 @@ public class Database implements DataHandler {
* @param flush whether writing is allowed
*/
private synchronized void closeOpenFilesAndUnlock(boolean flush) throws SQLException {
if (log != null) {
stopWriter();
log = null;
}
stopWriter();
if (pageStore != null) {
if (flush) {
try {
......@@ -1305,10 +1296,6 @@ public class Database implements DataHandler {
return databaseName;
}
public LogSystem getLog() {
return log;
}
/**
* Get all sessions that are currently connected to the database.
*
......@@ -1636,6 +1623,70 @@ public class Database implements DataHandler {
writeDelay = value;
if (writer != null) {
writer.setWriteDelay(value);
// TODO check if MIN_WRITE_DELAY is a good value
flushOnEachCommit = writeDelay < SysProperties.MIN_WRITE_DELAY;
}
}
/**
* Check if flush-on-each-commit is enabled.
*
* @return true if it is
*/
public boolean getFlushOnEachCommit() {
return flushOnEachCommit;
}
/**
* Get the list of in-doubt transactions.
*
* @return the list
*/
public ObjectArray<InDoubtTransaction> getInDoubtTransactions() {
return pageStore == null ? null : pageStore.getInDoubtTransactions();
}
/**
* Prepare a transaction.
*
* @param session the session
* @param transaction the name of the transaction
*/
public void prepareCommit(Session session, String transaction) throws SQLException {
if (readOnly) {
return;
}
synchronized (this) {
pageStore.prepareCommit(session, transaction);
}
}
/**
* Commit the current transaction of the given session.
*
* @param session the session
*/
public void commit(Session session) throws SQLException {
if (readOnly) {
return;
}
synchronized (this) {
if (pageStore != null) {
pageStore.commit(session);
}
session.setAllCommitted();
}
}
/**
* Flush all pending changes to the transaction log files.
*/
public void flush() throws SQLException {
if (readOnly) {
return;
}
synchronized (this) {
pageStore.flushLog();
}
}
......
......@@ -17,8 +17,8 @@ import org.h2.constant.SysProperties;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.store.FileLock;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.RandomUtils;
import org.h2.util.StringUtils;
/**
......@@ -226,7 +226,7 @@ public class Engine {
synchronized (INSTANCE) {
// delay up to the last delay
// an attacker can't know how long it will be
delay = RandomUtils.nextSecureInt((int) delay);
delay = MathUtils.secureRandomInt((int) delay);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
......@@ -250,7 +250,7 @@ public class Engine {
}
if (min > 0) {
// a bit more to protect against timing attacks
delay += Math.abs(RandomUtils.getSecureLong() % 100);
delay += Math.abs(MathUtils.secureRandomLong() % 100);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
......
......@@ -21,10 +21,6 @@ import org.h2.constant.SysProperties;
import org.h2.constraint.Constraint;
import org.h2.index.Index;
import org.h2.jdbc.JdbcConnection;
import org.h2.log.InDoubtTransaction;
import org.h2.log.LogSystem;
import org.h2.log.UndoLog;
import org.h2.log.UndoLogRecord;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
......@@ -32,6 +28,7 @@ import org.h2.result.ResultInterface;
import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.store.DataHandler;
import org.h2.store.InDoubtTransaction;
import org.h2.table.Table;
import org.h2.util.New;
import org.h2.util.ObjectArray;
......@@ -46,7 +43,12 @@ import org.h2.value.ValueString;
* mode, this object resides on the server side and communicates with a
* SessionRemote object on the client side.
*/
public class Session extends SessionWithState {
public class Session extends SessionWithState implements SessionFactory {
/**
* This special log position means that the log entry has been written.
*/
public static final int LOG_WRITTEN = -1;
/**
* The prefix of generated identifiers. It may not have letters, because
......@@ -64,12 +66,11 @@ public class Session extends SessionWithState {
private UndoLog undoLog;
private boolean autoCommit = true;
private Random random;
private LogSystem logSystem;
private int lockTimeout;
private Value lastIdentity = ValueLong.get(0);
private Value scopeIdentity = ValueLong.get(0);
private int firstUncommittedLog = LogSystem.LOG_WRITTEN;
private int firstUncommittedPos = LogSystem.LOG_WRITTEN;
private int firstUncommittedLog = Session.LOG_WRITTEN;
private int firstUncommittedPos = Session.LOG_WRITTEN;
private HashMap<String, Integer> savepoints;
private Exception stackTrace = new Exception();
private HashMap<String, Table> localTempTables;
......@@ -102,17 +103,24 @@ public class Session extends SessionWithState {
private int modificationIdState;
private int objectId;
public Session() {
// to create a new session using the factory
}
public Session(Database database, User user, int id) {
this.database = database;
this.undoLog = new UndoLog(this);
this.user = user;
this.id = id;
this.logSystem = database.getLog();
Setting setting = database.findSetting(SetTypes.getTypeName(SetTypes.DEFAULT_LOCK_TIMEOUT));
this.lockTimeout = setting == null ? Constants.INITIAL_LOCK_TIMEOUT : setting.getIntValue();
this.currentSchemaName = Constants.SCHEMA_MAIN;
}
public SessionInterface createSession(ConnectionInfo ci) throws SQLException {
return Engine.getInstance().getSession(ci);
}
public boolean setCommitOrRollbackDisabled(boolean x) {
boolean old = commitOrRollbackDisabled;
commitOrRollbackDisabled = x;
......@@ -441,7 +449,7 @@ public class Session extends SessionWithState {
if (containsUncommitted()) {
// need to commit even if rollback is not possible
// (create/drop table and so on)
logSystem.commit(this);
database.commit(this);
}
if (undoLog.size() > 0) {
// commit the rows, even when not using MVCC
......@@ -472,7 +480,7 @@ public class Session extends SessionWithState {
if (unlinkMap != null && unlinkMap.size() > 0) {
// need to flush the log file, because we can't unlink lobs if the
// commit record is not written
logSystem.flush();
database.flush();
for (Value v : unlinkMap.values()) {
v.unlink();
}
......@@ -499,7 +507,7 @@ public class Session extends SessionWithState {
needCommit = true;
}
if (locks.size() > 0 || needCommit) {
logSystem.commit(this);
database.commit(this);
}
cleanTempTables(false);
unlockAll();
......@@ -695,7 +703,7 @@ public class Session extends SessionWithState {
* @param pos the position of the log entry in the log file
*/
public void addLogPos(int logId, int pos) {
if (firstUncommittedLog == LogSystem.LOG_WRITTEN) {
if (firstUncommittedLog == Session.LOG_WRITTEN) {
firstUncommittedLog = logId;
firstUncommittedPos = pos;
}
......@@ -713,12 +721,12 @@ public class Session extends SessionWithState {
* This method is called after the log file has committed this session.
*/
public void setAllCommitted() {
firstUncommittedLog = LogSystem.LOG_WRITTEN;
firstUncommittedPos = LogSystem.LOG_WRITTEN;
firstUncommittedLog = Session.LOG_WRITTEN;
firstUncommittedPos = Session.LOG_WRITTEN;
}
private boolean containsUncommitted() {
return firstUncommittedLog != LogSystem.LOG_WRITTEN;
return firstUncommittedLog != Session.LOG_WRITTEN;
}
/**
......@@ -760,7 +768,7 @@ public class Session extends SessionWithState {
if (containsUncommitted()) {
// need to commit even if rollback is not possible (create/drop
// table and so on)
logSystem.prepareCommit(this, transactionName);
database.prepareCommit(this, transactionName);
}
currentTransactionName = transactionName;
}
......@@ -779,7 +787,7 @@ public class Session extends SessionWithState {
rollback();
}
} else {
ObjectArray<InDoubtTransaction> list = logSystem.getInDoubtTransactions();
ObjectArray<InDoubtTransaction> list = database.getInDoubtTransactions();
int state = commit ? InDoubtTransaction.COMMIT : InDoubtTransaction.ROLLBACK;
boolean found = false;
for (int i = 0; list != null && i < list.size(); i++) {
......
/*
* Copyright 2004-2010 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.engine;
import java.sql.SQLException;
/**
* A factory for embedded database sessions.
*/
public class SessionFactoryEmbedded implements SessionFactory {
public SessionInterface createSession(ConnectionInfo ci) throws SQLException {
return Engine.getInstance().getSession(ci);
}
}
......@@ -27,9 +27,9 @@ import org.h2.store.FileStore;
import org.h2.util.ByteUtils;
import org.h2.util.ClassUtils;
import org.h2.util.FileUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.ObjectArray;
import org.h2.util.RandomUtils;
import org.h2.util.SmallLRUCache;
import org.h2.util.StringUtils;
import org.h2.util.TempFileDeleter;
......@@ -231,7 +231,7 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D
backup = (ConnectionInfo) ci.clone();
connectionInfo = (ConnectionInfo) ci.clone();
}
SessionFactory sf = (SessionFactory) ClassUtils.loadSystemClass("org.h2.engine.SessionFactoryEmbedded").newInstance();
SessionFactory sf = (SessionFactory) ClassUtils.loadSystemClass("org.h2.engine.Session").newInstance();
if (openNew) {
ci.setProperty("OPEN_NEW", "true");
}
......@@ -317,7 +317,7 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D
}
cipher = ci.getProperty("CIPHER");
if (cipher != null) {
fileEncryptionKey = RandomUtils.getSecureBytes(32);
fileEncryptionKey = MathUtils.secureRandomBytes(32);
}
String[] servers = StringUtils.arraySplit(server, ',', true);
int len = servers.length;
......@@ -366,7 +366,7 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D
// ignore
}
if (clientVersion >= Constants.TCP_PROTOCOL_VERSION) {
sessionId = ByteUtils.convertBytesToString(RandomUtils.getSecureBytes(32));
sessionId = ByteUtils.convertBytesToString(MathUtils.secureRandomBytes(32));
synchronized (this) {
for (Transfer transfer : transferList) {
try {
......
/*
* Copyright 2004-2010 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.engine;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.message.Message;
import org.h2.store.Data;
import org.h2.store.FileStore;
import org.h2.util.ObjectArray;
/**
* Each session keeps a undo log if rollback is required.
*/
public class UndoLog {
private Database database;
// TODO undo log entry: a chain would probably be faster
// and use less memory than an array
private ObjectArray<UndoLogRecord> records = ObjectArray.newInstance();
private FileStore file;
private Data rowBuff;
private int memoryUndo;
/**
* Create a new undo log for the given session.
*
* @param session the session
*/
public UndoLog(Session session) {
this.database = session.getDatabase();
}
/**
* Get the number of active rows in this undo log.
*
* @return the number of rows
*/
public int size() {
if (SysProperties.CHECK && memoryUndo > records.size()) {
Message.throwInternalError();
}
return records.size();
}
/**
* Clear the undo log. This method is called after the transaction is
* committed.
*/
public void clear() {
records.clear();
memoryUndo = 0;
if (file != null) {
file.closeAndDeleteSilently();
file = null;
rowBuff = null;
}
}
/**
* Get the last record and remove it from the list of operations.
*
* @return the last record
*/
public UndoLogRecord getLast() throws SQLException {
int i = records.size() - 1;
UndoLogRecord entry = records.get(i);
if (entry.isStored()) {
int start = Math.max(0, i - database.getMaxMemoryUndo() / 2);
UndoLogRecord first = null;
for (int j = start; j <= i; j++) {
UndoLogRecord e = records.get(j);
if (e.isStored()) {
e.load(rowBuff, file);
memoryUndo++;
if (first == null) {
first = e;
}
}
}
for (int k = 0; k < i; k++) {
UndoLogRecord e = records.get(k);
e.invalidatePos();
}
first.seek(file);
}
return entry;
}
/**
* Remove the last record from the list of operations.
*
* @param trimToSize if the undo array should shrink to conserve memory
*/
public void removeLast(boolean trimToSize) {
int i = records.size() - 1;
UndoLogRecord r = (UndoLogRecord) records.remove(i);
if (!r.isStored()) {
memoryUndo--;
}
if (trimToSize && i > 1024 && (i & 1023) == 0) {
records.trimToSize();
}
}
/**
* Append an undo log entry to the log.
*
* @param entry the entry
*/
public void add(UndoLogRecord entry) throws SQLException {
records.add(entry);
if (!entry.isStored()) {
memoryUndo++;
}
if (memoryUndo > database.getMaxMemoryUndo() && database.isPersistent() && !database.isMultiVersion()) {
if (file == null) {
String fileName = database.createTempFile();
file = database.openFile(fileName, "rw", false);
file.seek(FileStore.HEADER_LENGTH);
rowBuff = Data.create(database, SysProperties.PAGE_SIZE);
Data buff = rowBuff;
for (int i = 0; i < records.size(); i++) {
UndoLogRecord r = records.get(i);
saveIfPossible(r, buff);
}
} else {
saveIfPossible(entry, rowBuff);
}
file.autoDelete();
}
}
private void saveIfPossible(UndoLogRecord r, Data buff) throws SQLException {
if (!r.isStored() && r.canStore()) {
r.save(buff, file);
memoryUndo--;
}
}
}
/*
* Copyright 2004-2010 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.engine;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.index.Index;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.store.Data;
import org.h2.store.FileStore;
import org.h2.table.Table;
import org.h2.value.Value;
/**
* An entry in a undo log.
*/
public class UndoLogRecord {
/**
* Operation type meaning the row was inserted.
*/
public static final short INSERT = 0;
/**
* Operation type meaning the row was deleted.
*/
public static final short DELETE = 1;
private static final int IN_MEMORY = 0, STORED = 1, IN_MEMORY_INVALID = 2;
private Table table;
private Row row;
private short operation;
private short state;
private int filePos;
/**
* Create a new undo log record
*
* @param table the table
* @param op the operation type
* @param row the row that was deleted or inserted
*/
public UndoLogRecord(Table table, short op, Row row) {
this.table = table;
this.row = row;
this.operation = op;
this.state = IN_MEMORY;
}
/**
* Check if the log record is stored in the file.
*
* @return true if it is
*/
boolean isStored() {
return state == STORED;
}
/**
* Check if this undo log record can be store. Only record can be stored if
* the table has a unique index.
*
* @return if it can be stored
*/
boolean canStore() {
return table.getUniqueIndex() != null;
}
/**
* Un-do the operation. If the row was inserted before, it is deleted now,
* and vice versa.
*
* @param session the session
*/
public void undo(Session session) throws SQLException {
Database db = session.getDatabase();
switch (operation) {
case INSERT:
if (state == IN_MEMORY_INVALID) {
state = IN_MEMORY;
}
if (db.getLockMode() == Constants.LOCK_MODE_OFF) {
if (row.isDeleted()) {
// it might have been deleted by another thread
return;
}
}
try {
row.setDeleted(false);
table.removeRow(session, row);
table.fireAfterRow(session, row, null, true);
} catch (SQLException e) {
if (session.getDatabase().getLockMode() == Constants.LOCK_MODE_OFF
&& e.getErrorCode() == ErrorCode.ROW_NOT_FOUND_WHEN_DELETING_1) {
// it might have been deleted by another thread
// ignore
} else {
throw e;
}
}
break;
case DELETE:
try {
table.addRow(session, row);
table.fireAfterRow(session, null, row, true);
// reset session id, otherwise other session think
// that this row was inserted by this session
row.commit();
} catch (SQLException e) {
if (session.getDatabase().getLockMode() == Constants.LOCK_MODE_OFF
&& e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
// it might have been added by another thread
// ignore
} else {
throw e;
}
}
break;
default:
Message.throwInternalError("op=" + operation);
}
}
/**
* Go to the right position in the file.
*
* @param file the file
*/
void seek(FileStore file) throws SQLException {
file.seek(((long) filePos) * Constants.FILE_BLOCK_SIZE);
}
/**
* Save the row in the file using the data page as a buffer.
*
* @param buff the buffer
* @param file the file
*/
void save(Data buff, FileStore file) throws SQLException {
buff.reset();
buff.writeInt(0);
buff.writeInt(operation);
buff.writeByte(row.isDeleted() ? (byte) 1 : (byte) 0);
buff.writeLong(row.getKey());
buff.writeInt(row.getSessionId());
buff.writeInt(row.getColumnCount());
for (int i = 0; i < row.getColumnCount(); i++) {
Value v = row.getValue(i);
buff.checkCapacity(buff.getValueLen(v));
buff.writeValue(v);
}
buff.fillAligned();
buff.setInt(0, buff.length() / Constants.FILE_BLOCK_SIZE);
filePos = (int) (file.getFilePointer() / Constants.FILE_BLOCK_SIZE);
file.write(buff.getBytes(), 0, buff.length());
row = null;
state = STORED;
}
/**
* Load an undo log record row using the data page as a buffer.
*
* @param buff the buffer
* @param file the source file
*/
void load(Data buff, FileStore file) throws SQLException {
int min = Constants.FILE_BLOCK_SIZE;
seek(file);
buff.reset();
file.readFully(buff.getBytes(), 0, min);
int len = buff.readInt() * Constants.FILE_BLOCK_SIZE;
buff.checkCapacity(len);
if (len - min > 0) {
file.readFully(buff.getBytes(), min, len - min);
}
int op = buff.readInt();
if (SysProperties.CHECK) {
if (operation != op) {
Message.throwInternalError("operation=" + operation + " op=" + op);
}
}
boolean deleted = buff.readByte() == 1;
long key = buff.readLong();
int sessionId = buff.readInt();
int columnCount = buff.readInt();
Value[] values = new Value[columnCount];
for (int i = 0; i < columnCount; i++) {
values[i] = buff.readValue();
}
row = new Row(values, 0);
row.setKey(key);
row.setDeleted(deleted);
row.setSessionId(sessionId);
state = IN_MEMORY_INVALID;
}
/**
* Get the table.
*
* @return the table
*/
public Table getTable() {
return table;
}
/**
* This method is called after the operation was committed.
* It commits the change to the indexes.
*/
public void commit() throws SQLException {
for (Index index : table.getIndexes()) {
index.commit(operation, row);
}
}
/**
* Get the row that was deleted or inserted.
*
* @return the row
*/
public Row getRow() {
return row;
}
/**
* Change the state from IN_MEMORY to IN_MEMORY_INVALID. This method is
* called if a later record was read from the temporary file, and therefore
* the position could have changed.
*/
void invalidatePos() {
if (this.state == IN_MEMORY) {
state = IN_MEMORY_INVALID;
}
}
}
......@@ -18,8 +18,8 @@ import org.h2.table.RangeTable;
import org.h2.table.Table;
import org.h2.table.TableView;
import org.h2.util.ByteUtils;
import org.h2.util.MathUtils;
import org.h2.util.ObjectArray;
import org.h2.util.RandomUtils;
import org.h2.util.StringUtils;
/**
......@@ -65,7 +65,7 @@ public class User extends RightOwner {
public void setUserPasswordHash(byte[] userPasswordHash) {
if (userPasswordHash != null) {
salt = new byte[Constants.SALT_LEN];
RandomUtils.nextBytes(salt);
MathUtils.randomBytes(salt);
SHA256 sha = new SHA256();
this.passwordHash = sha.getHashWithSalt(userPasswordHash, salt);
}
......
......@@ -47,7 +47,6 @@ import org.h2.util.MathUtils;
import org.h2.util.MemoryUtils;
import org.h2.util.New;
import org.h2.util.ObjectArray;
import org.h2.util.RandomUtils;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.value.DataType;
......@@ -501,7 +500,7 @@ public class Function extends Expression implements FunctionCall {
result = ValueDouble.get(Math.tan(v0.getDouble()));
break;
case SECURE_RAND:
result = ValueBytes.getNoCopy(RandomUtils.getSecureBytes(v0.getInt()));
result = ValueBytes.getNoCopy(MathUtils.secureRandomBytes(v0.getInt()));
break;
case EXPAND:
result = ValueBytes.getNoCopy(CompressTool.getInstance().expand(v0.getBytesNoCopy()));
......@@ -1171,7 +1170,7 @@ public class Function extends Expression implements FunctionCall {
}
private byte[] getPaddedArrayCopy(byte[] data, int blockSize) {
int size = MathUtils.roundUp(data.length, blockSize);
int size = MathUtils.roundUpInt(data.length, blockSize);
byte[] newData = MemoryUtils.newBytes(size);
System.arraycopy(data, 0, newData, 0, data.length);
return newData;
......
......@@ -281,7 +281,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
if (isMultiVersion) {
int v1 = rowData.getVersion();
int v2 = compare.getVersion();
return MathUtils.compare(v2, v1);
return MathUtils.compareInt(v2, v1);
}
return 0;
}
......
......@@ -141,7 +141,7 @@ public class MultiVersionCursor implements Cursor {
// version would be compared as well
long k1 = deltaRow.getKey();
long k2 = baseRow.getKey();
compare = MathUtils.compare(k1, k2);
compare = MathUtils.compareLong(k1, k2);
}
if (compare == 0) {
if (isDeleted) {
......
......@@ -15,7 +15,7 @@ import java.util.List;
import org.h2.constant.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.log.UndoLogRecord;
import org.h2.engine.UndoLogRecord;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
......
......@@ -14,7 +14,7 @@ import java.util.Iterator;
import java.util.List;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.log.UndoLogRecord;
import org.h2.engine.UndoLogRecord;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
......
......@@ -11,8 +11,8 @@ import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.engine.Database;
import org.h2.expression.Expression;
import org.h2.util.MathUtils;
import org.h2.util.ObjectArray;
import org.h2.util.RandomUtils;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.value.Value;
......@@ -169,7 +169,7 @@ public class SortOrder {
int i, j;
while (r - l > 10) {
// randomized pivot to avoid worst case
i = RandomUtils.nextInt(r - l - 4) + l + 2;
i = MathUtils.randomInt(r - l - 4) + l + 2;
if (compare(rows.get(l), rows.get(r)) > 0) {
swap(rows, l, r);
}
......
......@@ -11,7 +11,7 @@ import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
import org.h2.util.RandomUtils;
import org.h2.util.MathUtils;
/**
* A file store that encrypts all data before writing,
......@@ -37,7 +37,7 @@ public class SecureFileStore extends FileStore {
}
protected byte[] generateSalt() {
return RandomUtils.getSecureBytes(Constants.FILE_BLOCK_SIZE);
return MathUtils.secureRandomBytes(Constants.FILE_BLOCK_SIZE);
}
protected void initKey(byte[] salt) {
......
......@@ -27,7 +27,6 @@ import org.h2.engine.Constants;
import org.h2.message.Message;
import org.h2.message.TraceSystem;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.New;
......@@ -152,7 +151,7 @@ public class TcpServer implements Service {
} else if ("-tcpSSL".equals(a)) {
ssl = true;
} else if ("-tcpPort".equals(a)) {
port = MathUtils.decodeInt(args[++i]);
port = Integer.decode(args[++i]);
} else if ("-tcpPassword".equals(a)) {
managementPassword = args[++i];
} else if ("-baseDir".equals(a)) {
......@@ -381,7 +380,7 @@ public class TcpServer implements Service {
if (idx >= 0) {
p = p.substring(0, idx);
}
port = MathUtils.decodeInt(p);
port = Integer.decode(p);
}
String db = getManagementDbName(port);
try {
......
......@@ -21,7 +21,6 @@ import java.util.HashSet;
import java.util.Set;
import org.h2.engine.Constants;
import org.h2.server.Service;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.New;
......@@ -82,7 +81,7 @@ public class PgServer implements Service {
if ("-trace".equals(a)) {
trace = true;
} else if ("-pgPort".equals(a)) {
port = MathUtils.decodeInt(args[++i]);
port = Integer.decode(args[++i]);
} else if ("-baseDir".equals(a)) {
baseDir = args[++i];
} else if ("-pgAllowOthers".equals(a)) {
......
......@@ -61,7 +61,7 @@ public class ConnectionInfo implements Comparable<ConnectionInfo> {
}
public int compareTo(ConnectionInfo o) {
return MathUtils.compare(lastAccess, o.lastAccess);
return MathUtils.compareInt(lastAccess, o.lastAccess);
}
}
......@@ -27,7 +27,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import org.h2.api.DatabaseEventListener;
import org.h2.bnf.Bnf;
import org.h2.constant.ErrorCode;
......@@ -46,7 +45,6 @@ import org.h2.tools.RunScript;
import org.h2.tools.Script;
import org.h2.tools.SimpleResultSet;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.MemoryUtils;
import org.h2.util.New;
import org.h2.util.ObjectArray;
......@@ -310,7 +308,7 @@ public class WebApp implements DatabaseEventListener {
private String adminSave() {
try {
Properties prop = new SortedProperties();
int port = MathUtils.decodeInt((String) attributes.get("port"));
int port = Integer.decode((String) attributes.get("port"));
prop.setProperty("webPort", String.valueOf(port));
server.setPort(port);
boolean allowOthers = Boolean.valueOf((String) attributes.get("allowOthers")).booleanValue();
......@@ -1285,7 +1283,7 @@ public class WebApp implements DatabaseEventListener {
} else if (sql.startsWith("@LOOP")) {
sql = sql.substring("@LOOP".length()).trim();
int idx = sql.indexOf(' ');
int count = MathUtils.decodeInt(sql.substring(0, idx));
int count = Integer.decode(sql.substring(0, idx));
sql = sql.substring(idx).trim();
return executeLoop(conn, count, sql);
} else if (sql.startsWith("@EDIT")) {
......
......@@ -38,7 +38,6 @@ import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.New;
import org.h2.util.RandomUtils;
import org.h2.util.Resources;
import org.h2.util.SortedProperties;
......@@ -155,7 +154,7 @@ public class WebServer implements Service {
}
private String generateSessionId() {
byte[] buff = RandomUtils.getSecureBytes(16);
byte[] buff = MathUtils.secureRandomBytes(16);
return ByteUtils.convertBytesToString(buff);
}
......@@ -225,7 +224,7 @@ public class WebServer implements Service {
for (int i = 0; args != null && i < args.length; i++) {
String a = args[i];
if ("-webPort".equals(a)) {
port = MathUtils.decodeInt(args[++i]);
port = Integer.decode(args[++i]);
} else if ("-webSSL".equals(a)) {
ssl = true;
} else if ("-webAllowOthers".equals(a)) {
......
......@@ -497,7 +497,7 @@ public class Data {
writeByte((byte) (DOUBLE_0_1 + x));
} else {
writeByte((byte) type);
writeVarLong(MathUtils.reverse(Double.doubleToLongBits(x)));
writeVarLong(MathUtils.reverseLong(Double.doubleToLongBits(x)));
}
break;
}
......@@ -507,7 +507,7 @@ public class Data {
writeByte((byte) (FLOAT_0_1 + x));
} else {
writeByte((byte) type);
writeVarInt(MathUtils.reverse(Float.floatToIntBits(v.getFloat())));
writeVarInt(MathUtils.reverseInt(Float.floatToIntBits(v.getFloat())));
}
break;
}
......@@ -644,9 +644,9 @@ public class Data {
case DOUBLE_0_1 + 1:
return ValueDouble.get(1);
case Value.DOUBLE:
return ValueDouble.get(Double.longBitsToDouble(MathUtils.reverse(readVarLong())));
return ValueDouble.get(Double.longBitsToDouble(MathUtils.reverseLong(readVarLong())));
case Value.FLOAT:
return ValueFloat.get(Float.intBitsToFloat(MathUtils.reverse(readVarInt())));
return ValueFloat.get(Float.intBitsToFloat(MathUtils.reverseInt(readVarInt())));
case Value.BLOB:
case Value.CLOB: {
int smallLen = readVarInt();
......@@ -743,14 +743,14 @@ public class Data {
if (x == 0.0 || x == 1.0) {
return 1;
}
return 1 + getVarLongLen(MathUtils.reverse(Double.doubleToLongBits(x)));
return 1 + getVarLongLen(MathUtils.reverseLong(Double.doubleToLongBits(x)));
}
case Value.FLOAT: {
float x = v.getFloat();
if (x == 0.0f || x == 1.0f) {
return 1;
}
return 1 + getVarIntLen(MathUtils.reverse(Float.floatToIntBits(v.getFloat())));
return 1 + getVarIntLen(MathUtils.reverseInt(Float.floatToIntBits(v.getFloat())));
}
case Value.STRING: {
String s = v.getString();
......@@ -1028,7 +1028,7 @@ public class Data {
*/
public void fillAligned() {
// 0..6 > 8, 7..14 > 16, 15..22 > 24, ...
fill(MathUtils.roundUp(pos + 2, Constants.FILE_BLOCK_SIZE));
fill(MathUtils.roundUpInt(pos + 2, Constants.FILE_BLOCK_SIZE));
}
}
......@@ -644,7 +644,7 @@ public class DataPage {
public void fillAligned() {
// TODO datapage: fillAligned should not use a fixed constant '2'
// 0..6 > 8, 7..14 > 16, 15..22 > 24, ...
fill(MathUtils.roundUp(pos + 2, Constants.FILE_BLOCK_SIZE));
fill(MathUtils.roundUpInt(pos + 2, Constants.FILE_BLOCK_SIZE));
}
/**
......
......@@ -25,8 +25,8 @@ import org.h2.message.Trace;
import org.h2.message.TraceSystem;
import org.h2.store.fs.FileSystem;
import org.h2.util.ByteUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.RandomUtils;
import org.h2.util.SortedProperties;
import org.h2.value.Transfer;
......@@ -291,7 +291,7 @@ public class FileLock implements Runnable {
}
private void setUniqueId() {
byte[] bytes = RandomUtils.getSecureBytes(RANDOM_BYTES);
byte[] bytes = MathUtils.secureRandomBytes(RANDOM_BYTES);
String random = ByteUtils.convertBytesToString(bytes);
uniqueId = Long.toHexString(System.currentTimeMillis()) + random;
properties.setProperty("id", uniqueId);
......
/*
* Copyright 2004-2010 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.store;
import java.sql.SQLException;
import org.h2.message.Message;
/**
* Represents an in-doubt transaction (a transaction in the prepare phase).
*/
public class InDoubtTransaction {
/**
* The transaction state meaning this transaction is not committed yet, but
* also not rolled back (in-doubt).
*/
public static final int IN_DOUBT = 0;
/**
* The transaction state meaning this transaction is committed.
*/
public static final int COMMIT = 1;
/**
* The transaction state meaning this transaction is rolled back.
*/
public static final int ROLLBACK = 2;
// TODO 2-phase-commit: document sql statements and metadata table
private final PageStore store;
private final int sessionId;
private final int pos;
private final String transaction;
private int state;
/**
* Create a new in-doubt transaction info object.
*
* @param store the page store
* @param sessionId the session id
* @param pos the position
* @param transaction the transaction name
*/
public InDoubtTransaction(PageStore store, int sessionId, int pos, String transaction) {
this.store = store;
this.sessionId = sessionId;
this.pos = pos;
this.transaction = transaction;
this.state = IN_DOUBT;
}
/**
* Change the state of this transaction.
* This will also update the log file.
*
* @param state the new state
*/
public void setState(int state) throws SQLException {
switch(state) {
case COMMIT:
store.setInDoubtTransactionState(sessionId, pos, true);
break;
case ROLLBACK:
store.setInDoubtTransactionState(sessionId, pos, false);
break;
default:
Message.throwInternalError("state="+state);
}
this.state = state;
}
/**
* Get the state of this transaction as a text.
*
* @return the transaction state text
*/
public String getState() {
switch(state) {
case IN_DOUBT:
return "IN_DOUBT";
case COMMIT:
return "COMMIT";
case ROLLBACK:
return "ROLLBACK";
default:
throw Message.throwInternalError("state="+state);
}
}
/**
* Get the name of the transaction.
*
* @return the transaction name
*/
public String getTransaction() {
return transaction;
}
}
......@@ -15,9 +15,6 @@ import org.h2.compress.CompressLZF;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.engine.Session;
import org.h2.log.InDoubtTransaction;
import org.h2.log.LogSystem;
import org.h2.log.SessionState;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.result.Row;
......@@ -521,8 +518,7 @@ public class PageLog {
if (trace.isDebugEnabled()) {
trace.debug("log commit s:" + sessionId);
}
LogSystem log = store.getDatabase().getLog();
if (log == null) {
if (store.getDatabase().getPageStore() == null) {
// database already closed
return;
}
......@@ -530,7 +526,7 @@ public class PageLog {
buffer.writeByte((byte) COMMIT);
buffer.writeVarInt(sessionId);
write(buffer);
if (log.getFlushOnEachCommit()) {
if (store.getDatabase().getFlushOnEachCommit()) {
flush();
}
} catch (IOException e) {
......@@ -549,8 +545,7 @@ public class PageLog {
if (trace.isDebugEnabled()) {
trace.debug("log prepare commit s:" + session.getId() + " " + transaction);
}
LogSystem log = store.getDatabase().getLog();
if (log == null) {
if (store.getDatabase().getPageStore() == null) {
// database already closed
return;
}
......@@ -569,7 +564,7 @@ public class PageLog {
// store it on a separate log page
flushOut();
pageOut.fillPage();
if (log.getFlushOnEachCommit()) {
if (store.getDatabase().getFlushOnEachCommit()) {
flush();
}
} catch (IOException e) {
......
......@@ -30,8 +30,6 @@ import org.h2.index.PageDataNode;
import org.h2.index.PageDataOverflow;
import org.h2.index.PageDelegateIndex;
import org.h2.index.PageIndex;
import org.h2.log.InDoubtTransaction;
import org.h2.log.LogSystem;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.result.Row;
......@@ -560,7 +558,7 @@ public class PageStore implements CacheWriter {
for (int i = 0; i < sessions.length; i++) {
Session session = sessions[i];
int firstUncommitted = session.getFirstUncommittedLog();
if (firstUncommitted != LogSystem.LOG_WRITTEN) {
if (firstUncommitted != Session.LOG_WRITTEN) {
if (firstUncommitted < firstUncommittedSection) {
firstUncommittedSection = firstUncommitted;
}
......
/*
* Copyright 2004-2010 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.store;
/**
* The session state contains information about when was the last commit of a
* session. It is only used during recovery.
*/
public class SessionState {
/**
* The session id
*/
public int sessionId;
/**
* The last log file id where a commit for this session is found.
*/
public int lastCommitLog;
/**
* The position where a commit for this session is found.
*/
public int lastCommitPos;
/**
* The in-doubt transaction if there is one.
*/
public InDoubtTransaction inDoubtTransaction;
/**
* Check if this session state is already committed at this point.
*
* @param logId the log file id
* @param pos the position in the log file
* @return true if it is committed
*/
public boolean isCommitted(int logId, int pos) {
if (logId != lastCommitLog) {
return lastCommitLog > logId;
}
return lastCommitPos >= pos;
}
public String toString() {
return "sessionId:" + sessionId + " log:" + lastCommitLog + " pos:" + lastCommitPos + " inDoubt:" + inDoubtTransaction;
}
}
......@@ -11,7 +11,6 @@ import java.security.AccessControlException;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.engine.Database;
import org.h2.log.LogSystem;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
......@@ -47,14 +46,7 @@ public class WriterThread implements Runnable {
* @param writeDelay the new write delay
*/
public void setWriteDelay(int writeDelay) {
LogSystem log = getLog();
this.writeDelay = writeDelay;
// TODO check if MIN_WRITE_DELAY is a good value
if (writeDelay < SysProperties.MIN_WRITE_DELAY) {
log.setFlushOnEachCommit(true);
} else {
log.setFlushOnEachCommit(false);
}
}
/**
......@@ -79,33 +71,19 @@ public class WriterThread implements Runnable {
}
}
private LogSystem getLog() {
Database database = databaseRef.get();
if (database == null) {
return null;
}
LogSystem log = database.getLog();
return log;
}
public void run() {
while (!stop) {
Database database = databaseRef.get();
if (database == null) {
break;
}
int wait = writeDelay;
try {
if (database.isFileLockSerialized()) {
wait = SysProperties.MIN_WRITE_DELAY;
database.checkpointIfRequired();
} else {
LogSystem log = database.getLog();
if (log == null) {
break;
}
log.flush();
database.flush();
}
} catch (SQLException e) {
TraceSystem traceSystem = database.getTraceSystem();
......
......@@ -14,8 +14,8 @@ import java.util.Iterator;
import java.util.TreeMap;
import org.h2.message.Message;
import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
import org.h2.util.ObjectArray;
import org.h2.util.RandomUtils;
/**
* This file system keeps files fully in memory.
......@@ -92,7 +92,7 @@ public class FileSystemMemory extends FileSystem {
name += ".";
synchronized (MEMORY_FILES) {
for (int i = 0;; i++) {
String n = name + (RandomUtils.getSecureLong() >>> 1) + suffix;
String n = name + (MathUtils.secureRandomLong() >>> 1) + suffix;
if (!exists(n)) {
getMemoryFile(n);
return n;
......
......@@ -38,7 +38,6 @@ import org.h2.expression.ValueExpression;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.index.MetaIndex;
import org.h2.log.InDoubtTransaction;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
......@@ -48,6 +47,7 @@ import org.h2.schema.Schema;
import org.h2.schema.SchemaObject;
import org.h2.schema.Sequence;
import org.h2.schema.TriggerObject;
import org.h2.store.InDoubtTransaction;
import org.h2.store.PageStore;
import org.h2.tools.Csv;
import org.h2.util.MathUtils;
......@@ -1251,7 +1251,7 @@ public class MetaTable extends Table {
break;
}
case IN_DOUBT: {
ObjectArray<InDoubtTransaction> prepared = database.getLog().getInDoubtTransactions();
ObjectArray<InDoubtTransaction> prepared = database.getInDoubtTransactions();
for (int i = 0; prepared != null && i < prepared.size(); i++) {
InDoubtTransaction prep = prepared.get(i);
add(rows,
......
......@@ -18,11 +18,11 @@ import org.h2.engine.Constants;
import org.h2.engine.DbObject;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.log.UndoLogRecord;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.result.Row;
......
......@@ -17,11 +17,11 @@ import java.util.HashMap;
import org.h2.command.Prepared;
import org.h2.constant.ErrorCode;
import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.index.LinkedIndex;
import org.h2.jdbc.JdbcSQLException;
import org.h2.log.UndoLogRecord;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.RowList;
......
......@@ -36,7 +36,6 @@ import org.h2.util.Tool;
import java.io.IOException;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.util.StartBrowser;
/**
* Starts the H2 Console (web-) server, as well as the TCP and PG server.
......@@ -166,7 +165,7 @@ ShutdownHandler {
// because some people don't look at the output,
// but are wondering why nothing happens
if (browserStart) {
StartBrowser.openURL(web.getURL());
Server.openBrowser(web.getURL());
}
if (tcpStart) {
......@@ -407,7 +406,7 @@ ShutdownHandler {
private void startBrowser() {
if (web != null) {
StartBrowser.openURL(web.getURL());
Server.openBrowser(web.getURL());
}
}
//## AWT end ##
......
......@@ -46,9 +46,9 @@ public class ConvertTraceFile extends Tool {
if (other == this) {
return 0;
}
int c = MathUtils.compare(other.time, time);
int c = MathUtils.compareLong(other.time, time);
if (c == 0) {
c = MathUtils.compare(other.executeCount, executeCount);
c = MathUtils.compareInt(other.executeCount, executeCount);
if (c == 0) {
c = sql.compareTo(other.sql);
}
......
......@@ -46,8 +46,8 @@ import org.h2.util.ByteUtils;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.IntArray;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.RandomUtils;
import org.h2.util.SmallLRUCache;
import org.h2.util.StatementBuilder;
import org.h2.util.TempFileDeleter;
......@@ -937,7 +937,7 @@ public class Recover extends Tool implements DataHandler {
}
SHA256 sha = new SHA256();
byte[] userPasswordHash = sha.getKeyPasswordHash(userName, "".toCharArray());
byte[] salt = RandomUtils.getSecureBytes(Constants.SALT_LEN);
byte[] salt = MathUtils.secureRandomBytes(Constants.SALT_LEN);
byte[] passwordHash = sha.getHashWithSalt(userPasswordHash, salt);
StringBuilder buff = new StringBuilder();
buff.append("SALT '").
......
......@@ -6,9 +6,12 @@
*/
package org.h2.tools;
import java.io.IOException;
import java.net.URI;
import java.sql.Connection;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.message.Message;
import org.h2.message.TraceSystem;
import org.h2.server.Service;
......@@ -16,7 +19,7 @@ import org.h2.server.ShutdownHandler;
import org.h2.server.TcpServer;
import org.h2.server.pg.PgServer;
import org.h2.server.web.WebServer;
import org.h2.util.StartBrowser;
import org.h2.util.StringUtils;
import org.h2.util.Tool;
/**
......@@ -202,7 +205,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
// because some people don't look at the output,
// but are wondering why nothing happens
if (browserStart) {
StartBrowser.openURL(web.getURL());
Server.openBrowser(web.getURL());
}
if (result != null) {
throw result;
......@@ -438,6 +441,77 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
return service;
}
/**
* Open a new browser tab or window with the given URL.
*
* @param url the URL to open
*/
public static void openBrowser(String url) {
String osName = SysProperties.getStringSetting("os.name", "linux").toLowerCase();
Runtime rt = Runtime.getRuntime();
try {
String browser = SysProperties.BROWSER;
if (browser != null) {
if (browser.indexOf("%url") >= 0) {
String[] args = StringUtils.arraySplit(browser, ',', false);
for (int i = 0; i < args.length; i++) {
args[i] = StringUtils.replaceAll(args[i], "%url", url);
}
rt.exec(args);
} else if (osName.indexOf("windows") >= 0) {
rt.exec(new String[] { "cmd.exe", "/C", browser, url });
} else {
rt.exec(new String[] { browser, url });
}
return;
}
try {
Class< ? > desktopClass = Class.forName("java.awt.Desktop");
// Desktop.isDesktopSupported()
Boolean supported = (Boolean) desktopClass.
getMethod("isDesktopSupported").
invoke(null, new Object[0]);
URI uri = new URI(url);
if (supported) {
// Desktop.getDesktop();
Object desktop = desktopClass.getMethod("getDesktop").
invoke(null, new Object[0]);
// desktop.browse(uri);
desktopClass.getMethod("browse", URI.class).
invoke(desktop, uri);
return;
}
} catch (Exception e) {
// ignore
}
if (osName.indexOf("windows") >= 0) {
rt.exec(new String[] { "rundll32", "url.dll,FileProtocolHandler", url });
} else if (osName.indexOf("mac") >= 0) {
// Mac OS: to open a page with Safari, use "open -a Safari"
Runtime.getRuntime().exec(new String[] { "open", url });
} else {
String[] browsers = { "firefox", "mozilla-firefox", "mozilla", "konqueror", "netscape", "opera" };
boolean ok = false;
for (String b : browsers) {
try {
rt.exec(new String[] { b, url });
ok = true;
break;
} catch (Exception e) {
// ignore and try the next
}
}
if (!ok) {
// No success in detection.
System.out.println("Please open a browser and go to " + url);
}
}
} catch (IOException e) {
System.out.println("Failed to start a browser to open the URL " + url);
e.printStackTrace();
}
}
/**
* Start a web server and a browser that uses the given connection. The
* current transaction is preserved. This is specially useful to manually
......@@ -454,7 +528,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
server.web = web;
webServer.setShutdownHandler(server);
String url = webServer.addSession(conn);
StartBrowser.openURL(url);
Server.openBrowser(url);
while (!webServer.isStopped()) {
try {
Thread.sleep(1000);
......
......@@ -21,7 +21,7 @@ public abstract class CacheObject {
*/
static class CacheComparator implements Comparator<CacheObject> {
public int compare(CacheObject a, CacheObject b) {
return MathUtils.compare(a.getPos(), b.getPos());
return MathUtils.compareInt(a.getPos(), b.getPos());
}
}
......
......@@ -86,7 +86,7 @@ public class NetUtils {
int startIndex = server.startsWith("[") ? server.indexOf(']') : 0;
int idx = server.indexOf(':', startIndex);
if (idx >= 0) {
port = MathUtils.decodeInt(server.substring(idx + 1));
port = Integer.decode(server.substring(idx + 1));
server = server.substring(0, idx);
}
InetAddress address = InetAddress.getByName(server);
......
......@@ -297,7 +297,7 @@ public class ObjectArray<T> implements Iterable<T> {
T[] d = data;
while (right - left > 12) {
// randomized pivot to avoid worst case
int i = RandomUtils.nextInt(right - left - 4) + left + 2;
int i = MathUtils.randomInt(right - left - 4) + left + 2;
// d[left]: smallest, d[i]: highest, d[right]: median
if (comp.compare(d[left], d[i]) > 0) {
swap(left, i);
......
/*
* Copyright 2004-2010 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.util;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.SecureRandom;
import java.util.Random;
/**
* Utility class that supports random and secure random functions. In some
* systems SecureRandom initialization is very slow, a workaround is implemented
* here.
*/
public class RandomUtils {
/**
* The secure random object.
*/
static SecureRandom cachedSecureRandom;
/**
* True if the secure random object is seeded.
*/
static volatile boolean seeded;
private static final Random RANDOM = new Random();
private RandomUtils() {
// utility class
}
private static synchronized SecureRandom getSecureRandom() {
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 until after the initializer block exits
try {
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.
Runnable runnable = new Runnable() {
public void run() {
try {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] seed = sr.generateSeed(20);
synchronized (cachedSecureRandom) {
cachedSecureRandom.setSeed(seed);
seeded = true;
}
} catch (Exception e) {
// NoSuchAlgorithmException
warn("SecureRandom", e);
}
}
};
try {
Thread t = new Thread(runnable);
// let the process terminate even if generating the seed is really slow
t.setDaemon(true);
t.start();
Thread.yield();
try {
// normally, generateSeed takes less than 200 ms
t.join(400);
} catch (InterruptedException e) {
warn("InterruptedException", e);
}
if (!seeded) {
byte[] seed = generateAlternativeSeed();
// this never reduces randomness
synchronized (cachedSecureRandom) {
cachedSecureRandom.setSeed(seed);
}
}
} catch (SecurityException e) {
// workaround for the Google App Engine: don't use a thread
runnable.run();
generateAlternativeSeed();
}
} catch (Exception e) {
// NoSuchAlgorithmException
warn("SecureRandom", e);
cachedSecureRandom = new SecureRandom();
}
return cachedSecureRandom;
}
private static byte[] generateAlternativeSeed() {
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(bout);
// milliseconds
out.writeLong(System.currentTimeMillis());
// nanoseconds if available
try {
Method m = System.class.getMethod("nanoTime");
if (m != null) {
Object o = m.invoke(null);
out.writeUTF(o.toString());
}
} catch (Exception e) {
// nanoTime not found, this is ok (only exists for JDK 1.5 and higher)
}
// memory
out.writeInt(new Object().hashCode());
Runtime runtime = Runtime.getRuntime();
out.writeLong(runtime.freeMemory());
out.writeLong(runtime.maxMemory());
out.writeLong(runtime.totalMemory());
// environment
try {
out.writeUTF(System.getProperties().toString());
} catch (Exception e) {
warn("generateAlternativeSeed", e);
}
// host name and ip addresses (if any)
try {
// workaround for the Google App Engine: don't use InetAddress
Class< ? > inetAddressClass = Class.forName("java.net.InetAddress");
Object localHost = inetAddressClass.getMethod("getLocalHost").invoke(null);
String hostName = inetAddressClass.getMethod("getHostName").invoke(localHost).toString();
out.writeUTF(hostName);
Object[] list = (Object[]) inetAddressClass.getMethod("getAllByName", String.class).invoke(null, hostName);
Method getAddress = inetAddressClass.getMethod("getAddress");
for (Object o : list) {
out.write((byte[]) getAddress.invoke(o));
}
} catch (Throwable e) {
// on some system, InetAddress is not supported
// on some system, InetAddress.getLocalHost() doesn't work
// for some reason (incorrect configuration)
}
// timing (a second thread is already running usually)
for (int j = 0; j < 16; j++) {
int i = 0;
long end = System.currentTimeMillis();
while (end == System.currentTimeMillis()) {
i++;
}
out.writeInt(i);
}
out.close();
return bout.toByteArray();
} catch (IOException e) {
warn("generateAlternativeSeed", e);
return new byte[1];
}
}
/**
* Get a cryptographically secure pseudo random long value.
*
* @return the random long value
*/
public static long getSecureLong() {
SecureRandom sr = getSecureRandom();
synchronized (sr) {
return sr.nextLong();
}
}
/**
* Get a number of pseudo random bytes.
*
* @param bytes the target array
*/
public static void nextBytes(byte[] bytes) {
RANDOM.nextBytes(bytes);
}
/**
* Get a number of cryptographically secure pseudo random bytes.
*
* @param len the number of bytes
* @return the random bytes
*/
public static byte[] getSecureBytes(int len) {
if (len <= 0) {
len = 1;
}
byte[] buff = new byte[len];
SecureRandom sr = getSecureRandom();
synchronized (sr) {
sr.nextBytes(buff);
}
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 lowerThan the value returned will be lower than this value
* @return the random long value
*/
public static int nextSecureInt(int lowerThan) {
SecureRandom sr = getSecureRandom();
synchronized (sr) {
return sr.nextInt(lowerThan);
}
}
/**
* Print a message to system output if there was a problem initializing the
* random number generator.
*
* @param s the message to print
* @param t the stack trace
*/
static void warn(String s, Throwable t) {
// not a fatal problem, but maybe reduced security
System.out.println("RandomUtils warning: " + s);
if (t != null) {
t.printStackTrace();
}
}
}
......@@ -72,7 +72,7 @@ public class SortedProperties extends Properties {
public static int getIntProperty(Properties prop, String key, int def) {
String value = prop.getProperty(key, "" + def);
try {
return MathUtils.decodeInt(value);
return Integer.decode(value);
} catch (Exception e) {
TraceSystem.traceThrowable(e);
return def;
......
/*
* Copyright 2004-2010 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.util;
import java.io.IOException;
import java.net.URI;
import org.h2.constant.SysProperties;
/**
* This tool starts the browser with a specific URL.
*/
public class StartBrowser {
private StartBrowser() {
// utility class
}
/**
* Open a new browser tab or window with the given URL.
*
* @param url the URL to open
*/
public static void openURL(String url) {
String osName = SysProperties.getStringSetting("os.name", "linux").toLowerCase();
Runtime rt = Runtime.getRuntime();
try {
String browser = SysProperties.BROWSER;
if (browser != null) {
if (browser.indexOf("%url") >= 0) {
String[] args = StringUtils.arraySplit(browser, ',', false);
for (int i = 0; i < args.length; i++) {
args[i] = StringUtils.replaceAll(args[i], "%url", url);
}
rt.exec(args);
} else if (osName.indexOf("windows") >= 0) {
rt.exec(new String[] { "cmd.exe", "/C", browser, url });
} else {
rt.exec(new String[] { browser, url });
}
return;
}
try {
Class< ? > desktopClass = Class.forName("java.awt.Desktop");
// Desktop.isDesktopSupported()
Boolean supported = (Boolean) desktopClass.
getMethod("isDesktopSupported").
invoke(null, new Object[0]);
URI uri = new URI(url);
if (supported) {
// Desktop.getDesktop();
Object desktop = desktopClass.getMethod("getDesktop").
invoke(null, new Object[0]);
// desktop.browse(uri);
desktopClass.getMethod("browse", URI.class).
invoke(desktop, uri);
return;
}
} catch (Exception e) {
// ignore
}
if (osName.indexOf("windows") >= 0) {
rt.exec(new String[] { "rundll32", "url.dll,FileProtocolHandler", url });
} else if (osName.indexOf("mac") >= 0) {
// Mac OS: to open a page with Safari, use "open -a Safari"
Runtime.getRuntime().exec(new String[] { "open", url });
} else {
String[] browsers = { "firefox", "mozilla-firefox", "mozilla", "konqueror", "netscape", "opera" };
boolean ok = false;
for (String b : browsers) {
try {
rt.exec(new String[] { b, url });
ok = true;
break;
} catch (Exception e) {
// ignore and try the next
}
}
if (!ok) {
// No success in detection.
System.out.println("Please open a browser and go to " + url);
}
}
} catch (IOException e) {
System.out.println("Failed to start a browser to open the URL " + url);
e.printStackTrace();
}
}
}
/*
* Copyright 2004-2010 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.util;
import java.lang.ref.SoftReference;
import org.h2.constant.SysProperties;
/**
* The string cache helps re-use string objects and therefore save memory.
* It uses a soft reference cache to keep frequently used strings in memory.
* The effect is similar to calling String.intern(), but faster.
*/
public class StringCache {
private static final boolean ENABLED = true;
private static SoftReference<String[]> softCache = new SoftReference<String[]>(null);
private StringCache() {
// utility class
}
/**
* Get the string from the cache if possible. If the string has not been
* found, it is added to the cache. If there is such a string in the cache,
* that one is returned.
*
* @param s the original string
* @return a string with the same content, if possible from the cache
*/
public static String get(String s) {
if (!SysProperties.OBJECT_CACHE || !ENABLED) {
return s;
}
if (s == null) {
return s;
} else if (s.length() == 0) {
return "";
}
int hash = s.hashCode();
String[] cache = getCache();
if (cache != null) {
int index = hash & (SysProperties.OBJECT_CACHE_SIZE - 1);
String cached = cache[index];
if (cached != null) {
if (s.equals(cached)) {
return cached;
}
}
cache[index] = s;
}
return s;
}
/**
* Get a string from the cache, and if no such string has been found, create
* a new one with only this content. This solves out of memory problems if
* the string is a substring of another, large string. In Java, strings are
* shared, which could lead to memory problems. This avoid such problems.
*
* @param s the string
* @return a string that is guaranteed not be a substring of a large string
*/
public static String getNew(String s) {
if (!SysProperties.OBJECT_CACHE || !ENABLED) {
return s;
}
if (s == null) {
return s;
} else if (s.length() == 0) {
return "";
}
int hash = s.hashCode();
String[] cache = getCache();
int index = hash & (SysProperties.OBJECT_CACHE_SIZE - 1);
if (cache == null) {
return s;
}
String cached = cache[index];
if (cached != null) {
if (s.equals(cached)) {
return cached;
}
}
// create a new object that is not shared
// (to avoid out of memory if it is a substring of a big String)
// NOPMD
s = new String(s);
cache[index] = s;
return s;
}
private static String[] getCache() {
String[] cache;
// softCache can be null due to a Tomcat problem
// a workaround is disable the system property org.apache.
// catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES
if (softCache != null) {
cache = softCache.get();
if (cache != null) {
return cache;
}
}
try {
cache = new String[SysProperties.OBJECT_CACHE_SIZE];
} catch (OutOfMemoryError e) {
return null;
}
softCache = new SoftReference<String[]>(cache);
return cache;
}
/**
* Clear the cache. This method is used for testing.
*/
public static void clearCache() {
softCache = new SoftReference<String[]>(null);
}
}
......@@ -6,6 +6,7 @@
*/
package org.h2.util;
import java.lang.ref.SoftReference;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
......@@ -23,10 +24,32 @@ import org.h2.message.Message;
*/
public class StringUtils {
private static SoftReference<String[]> softCache = new SoftReference<String[]>(null);
private StringUtils() {
// utility class
}
private static String[] getCache() {
String[] cache;
// softCache can be null due to a Tomcat problem
// a workaround is disable the system property org.apache.
// catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES
if (softCache != null) {
cache = softCache.get();
if (cache != null) {
return cache;
}
}
try {
cache = new String[SysProperties.OBJECT_CACHE_SIZE];
} catch (OutOfMemoryError e) {
return null;
}
softCache = new SoftReference<String[]>(cache);
return cache;
}
/**
* Check if two strings are equal. Here, null is equal to null.
*
......@@ -883,4 +906,81 @@ public class StringUtils {
return s;
}
/**
* Get the string from the cache if possible. If the string has not been
* found, it is added to the cache. If there is such a string in the cache,
* that one is returned.
*
* @param s the original string
* @return a string with the same content, if possible from the cache
*/
public static String cache(String s) {
if (!SysProperties.OBJECT_CACHE) {
return s;
}
if (s == null) {
return s;
} else if (s.length() == 0) {
return "";
}
int hash = s.hashCode();
String[] cache = getCache();
if (cache != null) {
int index = hash & (SysProperties.OBJECT_CACHE_SIZE - 1);
String cached = cache[index];
if (cached != null) {
if (s.equals(cached)) {
return cached;
}
}
cache[index] = s;
}
return s;
}
/**
* Get a string from the cache, and if no such string has been found, create
* a new one with only this content. This solves out of memory problems if
* the string is a substring of another, large string. In Java, strings are
* shared, which could lead to memory problems. This avoid such problems.
*
* @param s the string
* @return a string that is guaranteed not be a substring of a large string
*/
public static String fromCacheOrNew(String s) {
if (!SysProperties.OBJECT_CACHE) {
return s;
}
if (s == null) {
return s;
} else if (s.length() == 0) {
return "";
}
int hash = s.hashCode();
String[] cache = getCache();
int index = hash & (SysProperties.OBJECT_CACHE_SIZE - 1);
if (cache == null) {
return s;
}
String cached = cache[index];
if (cached != null) {
if (s.equals(cached)) {
return cached;
}
}
// create a new object that is not shared
// (to avoid out of memory if it is a substring of a big String)
// NOPMD
s = new String(s);
cache[index] = s;
return s;
}
/**
* Clear the cache. This method is used for testing.
*/
public static void clearCache() {
softCache = new SoftReference<String[]>(null);
}
}
......@@ -34,7 +34,7 @@ import org.h2.util.ExactUTF8InputStreamReader;
import org.h2.util.IOUtils;
import org.h2.util.MemoryUtils;
import org.h2.util.NetUtils;
import org.h2.util.StringCache;
import org.h2.util.StringUtils;
/**
* The transfer class is used to send and receive Value objects.
......@@ -241,7 +241,7 @@ public class Transfer {
buff.append(in.readChar());
}
String s = buff.toString();
s = StringCache.get(s);
s = StringUtils.cache(s);
return s;
}
......
......@@ -17,7 +17,6 @@ import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.message.Message;
......@@ -25,7 +24,6 @@ import org.h2.store.DataHandler;
import org.h2.tools.SimpleResultSet;
import org.h2.util.ByteUtils;
import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/**
......@@ -734,13 +732,13 @@ public abstract class Value {
}
}
case BYTE:
return ValueByte.get(MathUtils.decodeByte(s.trim()));
return ValueByte.get(Byte.decode(s.trim()));
case SHORT:
return ValueShort.get(MathUtils.decodeShort(s.trim()));
return ValueShort.get(Short.decode(s.trim()));
case INT:
return ValueInt.get(MathUtils.decodeInt(s.trim()));
return ValueInt.get(Integer.decode(s.trim()));
case LONG:
return ValueLong.get(MathUtils.decodeLong(s.trim()));
return ValueLong.get(Long.decode(s.trim()));
case DECIMAL:
return ValueDecimal.get(new BigDecimal(s.trim()));
case TIME:
......
......@@ -86,7 +86,7 @@ public class ValueByte extends Value {
protected int compareSecure(Value o, CompareMode mode) {
ValueByte v = (ValueByte) o;
return MathUtils.compare(value, v.value);
return MathUtils.compareInt(value, v.value);
}
public String getString() {
......
......@@ -6,19 +6,26 @@
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.util.ByteUtils;
import org.h2.util.MathUtils;
import org.h2.util.MemoryUtils;
/**
* Implementation of the BINARY data type.
* It is also the base class for ValueJavaObject.
*/
public class ValueBytes extends ValueBytesBase {
public class ValueBytes extends Value {
private static final ValueBytes EMPTY = new ValueBytes(MemoryUtils.EMPTY_BYTES);
private final byte[] value;
private int hash;
protected ValueBytes(byte[] v) {
super(v);
this.value = v;
}
/**
......@@ -58,4 +65,56 @@ public class ValueBytes extends ValueBytesBase {
return Value.BYTES;
}
public String getSQL() {
return "X'" + getString() + "'";
}
public byte[] getBytesNoCopy() {
return value;
}
public byte[] getBytes() {
return ByteUtils.cloneByteArray(value);
}
protected int compareSecure(Value v, CompareMode mode) {
byte[] v2 = ((ValueBytes) v).value;
return ByteUtils.compareNotNull(value, v2);
}
public String getString() {
return ByteUtils.convertBytesToString(value);
}
public long getPrecision() {
return value.length;
}
public int hashCode() {
if (hash == 0) {
hash = ByteUtils.getByteArrayHash(value);
}
return hash;
}
public Object getObject() {
return getBytes();
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setBytes(parameterIndex, value);
}
public int getDisplaySize() {
return MathUtils.convertLongToInt(value.length * 2L);
}
public int getMemory() {
return value.length + 4;
}
public boolean equals(Object other) {
return other instanceof ValueBytes && ByteUtils.compareNotNull(value, ((ValueBytes) other).value) == 0;
}
}
/*
* Copyright 2004-2010 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.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.util.ByteUtils;
import org.h2.util.MathUtils;
/**
* This is the base class for ValueBytes and ValueJavaObject.
*/
abstract class ValueBytesBase extends Value {
private final byte[] value;
private int hash;
protected ValueBytesBase(byte[] v) {
this.value = v;
}
public String getSQL() {
return "X'" + getString() + "'";
}
public byte[] getBytesNoCopy() {
return value;
}
public byte[] getBytes() {
return ByteUtils.cloneByteArray(value);
}
protected int compareSecure(Value v, CompareMode mode) {
byte[] v2 = ((ValueBytesBase) v).value;
return ByteUtils.compareNotNull(value, v2);
}
public String getString() {
return ByteUtils.convertBytesToString(value);
}
public long getPrecision() {
return value.length;
}
public int hashCode() {
if (hash == 0) {
hash = ByteUtils.getByteArrayHash(value);
}
return hash;
}
public Object getObject() {
return getBytes();
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setBytes(parameterIndex, value);
}
public int getDisplaySize() {
return MathUtils.convertLongToInt(value.length * 2L);
}
public int getMemory() {
return value.length + 4;
}
public boolean equals(Object other) {
return other instanceof ValueBytesBase && ByteUtils.compareNotNull(value, ((ValueBytesBase) other).value) == 0;
}
}
......@@ -120,7 +120,7 @@ public class ValueInt extends Value {
protected int compareSecure(Value o, CompareMode mode) {
ValueInt v = (ValueInt) o;
return MathUtils.compare(value, v.value);
return MathUtils.compareInt(value, v.value);
}
public String getString() {
......
......@@ -17,7 +17,7 @@ import org.h2.util.MemoryUtils;
/**
* Implementation of the OBJECT data type.
*/
public class ValueJavaObject extends ValueBytesBase {
public class ValueJavaObject extends ValueBytes {
private static final ValueJavaObject EMPTY = new ValueJavaObject(MemoryUtils.EMPTY_BYTES);
......
......@@ -28,7 +28,6 @@ import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
import org.h2.util.MemoryUtils;
import org.h2.util.RandomUtils;
import org.h2.util.SmallLRUCache;
import org.h2.util.StringUtils;
......@@ -289,7 +288,7 @@ public class ValueLob extends Value {
if (newId > Integer.MAX_VALUE / lobsPerDir) {
// this directory path is full: start from zero
newId = 0;
dirCounter = RandomUtils.nextInt(lobsPerDir - 1) * lobsPerDir;
dirCounter = MathUtils.randomInt(lobsPerDir - 1) * lobsPerDir;
} else {
// calculate the directory
// start with 1 (otherwise we don't know the number of directories)
......
......@@ -141,7 +141,7 @@ public class ValueLong extends Value {
protected int compareSecure(Value o, CompareMode mode) {
ValueLong v = (ValueLong) o;
return MathUtils.compare(value, v.value);
return MathUtils.compareLong(value, v.value);
}
public String getString() {
......
......@@ -86,7 +86,7 @@ public class ValueShort extends Value {
protected int compareSecure(Value o, CompareMode mode) {
ValueShort v = (ValueShort) o;
return MathUtils.compare(value, v.value);
return MathUtils.compareInt(value, v.value);
}
public String getString() {
......
......@@ -6,33 +6,73 @@
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.util.MathUtils;
import org.h2.util.StringCache;
import org.h2.util.StringUtils;
/**
* Implementation of the VARCHAR data type.
* It is also the base class for other ValueString* classes.
*/
public class ValueString extends ValueStringBase {
public class ValueString extends Value {
private static final ValueString EMPTY = new ValueString("");
/**
* The string data.
*/
protected final String value;
protected ValueString(String value) {
super(value);
this.value = value;
}
public int getType() {
return Value.STRING;
public String getSQL() {
return StringUtils.quoteStringSQL(value);
}
public boolean equals(Object other) {
return other instanceof ValueString && value.equals(((ValueString) other).value);
}
protected int compareSecure(Value o, CompareMode mode) {
// compatibility: the other object could be ValueStringFixed
ValueStringBase v = (ValueStringBase) o;
// compatibility: the other object could be another type
ValueString v = (ValueString) o;
return mode.compareString(value, v.value, false);
}
public boolean equals(Object other) {
return other instanceof ValueStringBase && value.equals(((ValueStringBase) other).value);
public String getString() {
return value;
}
public long getPrecision() {
return value.length();
}
public Object getObject() {
return value;
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setString(parameterIndex, value);
}
public int getDisplaySize() {
return value.length();
}
public int getMemory() {
return value.length() * 2 + 30;
}
public Value convertPrecision(long precision) {
if (precision == 0 || value.length() <= precision) {
return this;
}
int p = MathUtils.convertLongToInt(precision);
return getNew(value.substring(0, p));
}
public int hashCode() {
......@@ -66,12 +106,8 @@ public class ValueString extends ValueStringBase {
}
public Value convertPrecision(long precision) {
if (precision == 0 || value.length() <= precision) {
return this;
}
int p = MathUtils.convertLongToInt(precision);
return ValueString.get(value.substring(0, p));
public int getType() {
return Value.STRING;
}
/**
......@@ -84,7 +120,7 @@ public class ValueString extends ValueStringBase {
if (s.length() == 0) {
return EMPTY;
}
ValueString obj = new ValueString(StringCache.get(s));
ValueString obj = new ValueString(StringUtils.cache(s));
if (s.length() > SysProperties.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE) {
return obj;
}
......@@ -93,4 +129,8 @@ public class ValueString extends ValueStringBase {
// return new ValueString(s.intern());
}
protected Value getNew(String s) {
return ValueString.get(s);
}
}
/*
* Copyright 2004-2010 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.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.util.StringUtils;
/**
* The base class for all ValueString* classes.
*/
abstract class ValueStringBase extends Value {
/**
* The string data.
*/
protected final String value;
protected ValueStringBase(String value) {
this.value = value;
}
public String getSQL() {
return StringUtils.quoteStringSQL(value);
}
public String getString() {
return value;
}
public long getPrecision() {
return value.length();
}
public Object getObject() {
return value;
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setString(parameterIndex, value);
}
public int getDisplaySize() {
return value.length();
}
public abstract Value convertPrecision(long precision);
public int getMemory() {
return value.length() * 2 + 30;
}
}
......@@ -7,13 +7,12 @@
package org.h2.value;
import org.h2.constant.SysProperties;
import org.h2.util.MathUtils;
import org.h2.util.StringCache;
import org.h2.util.StringUtils;
/**
* Implementation of the CHAR data type.
*/
public class ValueStringFixed extends ValueStringBase {
public class ValueStringFixed extends ValueString {
private static final ValueStringFixed EMPTY = new ValueStringFixed("");
......@@ -21,12 +20,6 @@ public class ValueStringFixed extends ValueStringBase {
super(value);
}
protected int compareSecure(Value o, CompareMode mode) {
// compatibility: the other object could be ValueString
ValueStringBase v = (ValueStringBase) o;
return mode.compareString(value, v.value, false);
}
private static String trimRight(String s) {
int endIndex = s.length() - 1;
int i = endIndex;
......@@ -37,16 +30,6 @@ public class ValueStringFixed extends ValueStringBase {
return s;
}
public boolean equals(Object other) {
return other instanceof ValueStringBase && value.equals(((ValueStringBase) other).value);
}
public int hashCode() {
// TODO hash performance: could build a quicker hash
// by hashing the size and a few characters
return value.hashCode();
}
public int getType() {
return Value.STRING_FIXED;
}
......@@ -63,19 +46,15 @@ public class ValueStringFixed extends ValueStringBase {
if (s.length() == 0) {
return EMPTY;
}
ValueStringFixed obj = new ValueStringFixed(StringCache.get(s));
ValueStringFixed obj = new ValueStringFixed(StringUtils.cache(s));
if (s.length() > SysProperties.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE) {
return obj;
}
return (ValueStringFixed) Value.cache(obj);
}
public Value convertPrecision(long precision) {
if (precision == 0 || value.length() <= precision) {
return this;
}
int p = MathUtils.convertLongToInt(precision);
return ValueStringFixed.get(value.substring(0, p));
protected Value getNew(String s) {
return ValueString.get(s);
}
}
......@@ -7,14 +7,12 @@
package org.h2.value;
import org.h2.constant.SysProperties;
import org.h2.util.MathUtils;
import org.h2.util.StringCache;
import org.h2.util.StringUtils;
/**
* Implementation of the VARCHAR_IGNORECASE data type.
*/
public class ValueStringIgnoreCase extends ValueStringBase {
public class ValueStringIgnoreCase extends ValueString {
private static final ValueStringIgnoreCase EMPTY = new ValueStringIgnoreCase("");
private int hash;
......@@ -33,7 +31,7 @@ public class ValueStringIgnoreCase extends ValueStringBase {
}
public boolean equals(Object other) {
return other instanceof ValueStringBase && value.equalsIgnoreCase(((ValueStringBase) other).value);
return other instanceof ValueString && value.equalsIgnoreCase(((ValueString) other).value);
}
public int hashCode() {
......@@ -59,7 +57,7 @@ public class ValueStringIgnoreCase extends ValueStringBase {
if (s.length() == 0) {
return EMPTY;
}
ValueStringIgnoreCase obj = new ValueStringIgnoreCase(StringCache.get(s));
ValueStringIgnoreCase obj = new ValueStringIgnoreCase(StringUtils.cache(s));
if (s.length() > SysProperties.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE) {
return obj;
}
......@@ -72,12 +70,8 @@ public class ValueStringIgnoreCase extends ValueStringBase {
return obj;
}
public Value convertPrecision(long precision) {
if (precision == 0 || value.length() <= precision) {
return this;
}
int p = MathUtils.convertLongToInt(precision);
return ValueStringIgnoreCase.get(value.substring(0, p));
protected Value getNew(String s) {
return ValueString.get(s);
}
}
......@@ -12,7 +12,6 @@ import java.util.UUID;
import org.h2.util.ByteUtils;
import org.h2.util.MathUtils;
import org.h2.util.RandomUtils;
import org.h2.util.StringUtils;
/**
......@@ -48,8 +47,8 @@ public class ValueUuid extends Value {
* @return the new UUID
*/
public static ValueUuid getNewRandom() {
long high = RandomUtils.getSecureLong();
long low = RandomUtils.getSecureLong();
long high = MathUtils.secureRandomLong();
long low = MathUtils.secureRandomLong();
// version 4 (random)
high = (high & (~0xf000L)) | 0x4000L;
// variant (Leach-Salz)
......@@ -151,7 +150,7 @@ public class ValueUuid extends Value {
}
ValueUuid v = (ValueUuid) o;
if (high == v.high) {
return MathUtils.compare(low, v.low);
return MathUtils.compareLong(low, v.low);
}
return high > v.high ? 1 : -1;
}
......
......@@ -287,9 +287,13 @@ java org.h2.test.TestAll timer
/*
check client jar file size
remove ObjectArray
simplify Message / ErrorCode / Resource
remove ObjectArray, BitField
flatten package hierarchy
Constants.FILE_BLOCK_SIZE and others
simplify SysProperties
remove SortedProperties
remove TempFileDeleter (UndoLog, ResultDiskBuffer, RowList, ValueLob)
combine small classes (StringCache / utils...)
document in performance section:
PreparedStatement prep = conn.prepareStatement(
......
......@@ -43,8 +43,8 @@ import org.h2.tools.Backup;
import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Restore;
import org.h2.util.FileUtils;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.RandomUtils;
/**
* A test that calls random methods with random parameters from JDBC objects.
......@@ -121,7 +121,7 @@ public class TestCrashAPI extends TestBase {
}
int len = getSize(2, 6);
for (int i = 0; i < len; i++) {
int seed = RandomUtils.nextInt(Integer.MAX_VALUE);
int seed = MathUtils.randomInt(Integer.MAX_VALUE);
testCase(seed);
deleteDb();
}
......
......@@ -18,7 +18,7 @@ import org.h2.store.fs.FileSystemMemory;
import org.h2.test.TestAll;
import org.h2.test.TestBase;
import org.h2.tools.DeleteDbFiles;
import org.h2.util.RandomUtils;
import org.h2.util.MathUtils;
/**
* This test executes random SQL statements generated using the BNF tool.
......@@ -203,7 +203,7 @@ public class TestRandomSQL extends TestBase {
exitOnError = false;
showSQL = false;
for (int a = 0; a < len; a++) {
int s = RandomUtils.nextInt(Integer.MAX_VALUE);
int s = MathUtils.randomInt(Integer.MAX_VALUE);
testCase(s);
}
}
......
......@@ -10,8 +10,8 @@ import java.util.ArrayList;
import org.h2.test.TestAll;
import org.h2.test.TestBase;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.RandomUtils;
/**
* A test that generates random SQL statements against a number of databases
......@@ -339,7 +339,7 @@ public class TestSynth extends TestBase {
public void test() throws Exception {
while (true) {
int seed = RandomUtils.nextInt(Integer.MAX_VALUE);
int seed = MathUtils.randomInt(Integer.MAX_VALUE);
testCase(seed);
}
}
......
......@@ -11,6 +11,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import org.h2.test.TestBase;
import org.h2.util.MathUtils;
import org.h2.util.New;
/**
......@@ -22,16 +23,16 @@ import org.h2.util.New;
public class TestClearReferences extends TestBase {
private static final String[] KNOWN_REFRESHED = {
"org.h2.compress.CompressLZF.cachedHashTable",
"org.h2.util.DateTimeUtils.cachedCalendar",
"org.h2.util.StringCache.softCache",
"org.h2.util.StringUtils.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",
"org.h2.util.MathUtils.cachedSecureRandom",
"org.h2.value.CompareMode.lastUsed"
};
......@@ -47,6 +48,9 @@ public class TestClearReferences extends TestBase {
}
public void test() throws Exception {
// initialize the known classes
MathUtils.secureRandomLong();
ArrayList<Class < ? >> classes = New.arrayList();
check(classes, new File("bin/org/h2"));
for (Class< ? > clazz : classes) {
......@@ -137,6 +141,13 @@ public class TestClearReferences extends TestBase {
if (o == null) {
continue;
}
// loadedByThisOrChild
if (o.getClass().getName().startsWith("java.lang.")) {
continue;
}
if (o.getClass().isArray() && o.getClass().getComponentType().isPrimitive()) {
continue;
}
clearField(instance.getClass().getName() + "." + field.getName() + " = " + o);
}
}
......
......@@ -31,35 +31,35 @@ public class TestMathUtils extends TestBase {
}
private void testReverse() {
assertEquals(Integer.reverse(0), MathUtils.reverse(0));
assertEquals(Integer.reverse(Integer.MAX_VALUE), MathUtils.reverse(Integer.MAX_VALUE));
assertEquals(Integer.reverse(Integer.MIN_VALUE), MathUtils.reverse(Integer.MIN_VALUE));
assertEquals(Long.reverse(0), MathUtils.reverse(0L));
assertEquals(Long.reverse(Long.MAX_VALUE), MathUtils.reverse(Long.MAX_VALUE));
assertEquals(Long.reverse(Long.MIN_VALUE), MathUtils.reverse(Long.MIN_VALUE));
assertEquals(Integer.reverse(0), MathUtils.reverseInt(0));
assertEquals(Integer.reverse(Integer.MAX_VALUE), MathUtils.reverseInt(Integer.MAX_VALUE));
assertEquals(Integer.reverse(Integer.MIN_VALUE), MathUtils.reverseInt(Integer.MIN_VALUE));
assertEquals(Long.reverse(0), MathUtils.reverseLong(0L));
assertEquals(Long.reverse(Long.MAX_VALUE), MathUtils.reverseLong(Long.MAX_VALUE));
assertEquals(Long.reverse(Long.MIN_VALUE), MathUtils.reverseLong(Long.MIN_VALUE));
for (int i = Integer.MIN_VALUE; i < 0; i += 1019) {
int x = MathUtils.reverse(i);
int x = MathUtils.reverseInt(i);
assertEquals(Integer.reverse(i), x);
}
for (int i = 0; i > 0; i += 1019) {
int x = MathUtils.reverse(i);
int x = MathUtils.reverseInt(i);
assertEquals(Integer.reverse(i), x);
}
for (long i = Long.MIN_VALUE; i < 0; i += 1018764321251L) {
long x = MathUtils.reverse(i);
long x = MathUtils.reverseLong(i);
assertEquals(Long.reverse(i), x);
}
for (long i = 0; i > 0; i += 1018764321251L) {
long x = MathUtils.reverse(i);
long x = MathUtils.reverseLong(i);
assertEquals(Long.reverse(i), x);
}
Random random = new Random(10);
for (int i = 0; i < 1000000; i++) {
long x = random.nextLong();
long r = MathUtils.reverse(x);
long r = MathUtils.reverseLong(x);
assertEquals(Long.reverse(x), r);
int y = random.nextInt();
int s = MathUtils.reverse(y);
int s = MathUtils.reverseInt(y);
assertEquals(Integer.reverse(y), s);
}
}
......
......@@ -9,7 +9,7 @@ package org.h2.test.unit;
import java.util.Random;
import org.h2.test.TestBase;
import org.h2.util.StringCache;
import org.h2.util.StringUtils;
/**
* Tests the string cache facility.
......@@ -37,11 +37,11 @@ public class TestStringCache extends TestBase {
public void test() throws InterruptedException {
returnNew = true;
StringCache.clearCache();
StringUtils.clearCache();
testSingleThread(getSize(5000, 20000));
testMultiThreads();
returnNew = false;
StringCache.clearCache();
StringUtils.clearCache();
testSingleThread(getSize(5000, 20000));
testMultiThreads();
}
......@@ -80,7 +80,7 @@ public class TestStringCache extends TestBase {
void testString() {
String a = randomString();
if (returnNew) {
String b = StringCache.getNew(a);
String b = StringUtils.fromCacheOrNew(a);
try {
assertEquals(a, b);
} catch (Exception e) {
......@@ -94,7 +94,7 @@ public class TestStringCache extends TestBase {
if (useIntern) {
b = a == null ? null : a.intern();
} else {
b = StringCache.get(a);
b = StringUtils.cache(a);
}
try {
assertEquals(a, b);
......
......@@ -12,8 +12,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import org.h2.tools.Server;
import org.h2.util.IOUtils;
import org.h2.util.StartBrowser;
import org.h2.util.StringUtils;
/**
......@@ -60,7 +60,7 @@ public class LinkChecker {
}
if (OPEN_EXTERNAL_LINKS) {
System.out.println(link);
StartBrowser.openURL(link);
Server.openBrowser(link);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
......
......@@ -23,7 +23,6 @@ import org.h2.store.fs.FileSystem;
import org.h2.tools.Server;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.SortedProperties;
import org.h2.util.Tool;
......@@ -328,7 +327,7 @@ public class FtpServer extends Tool implements Service {
for (int i = 0; args != null && i < args.length; i++) {
String a = args[i];
if ("-ftpPort".equals(a)) {
port = MathUtils.decodeInt(args[++i]);
port = Integer.decode(args[++i]);
} else if ("-ftpDir".equals(a)) {
root = FileUtils.normalize(args[++i]);
} else if ("-ftpRead".equals(a)) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论