@Override public void close() { RuntimeException closeError = null; if (transferList != null) { synchronized (this) { for (Transfer transfer : transferList) { try { traceOperation("SESSION_CLOSE", 0); transfer.writeInt(SessionRemote.SESSION_CLOSE); done(transfer); transfer.close(); } catch (RuntimeException e) { trace.error(e, "close"); closeError = e; } catch (Exception e) { trace.error(e, "close"); } } } transferList = null; } traceSystem.close(); if (embedded != null) { embedded.close(); embedded = null; } if (closeError != null) { throw closeError; } }
private void sendError(Throwable t) { try { SQLException e = DbException.convert(t).getSQLException(); StringWriter writer = new StringWriter(); e.printStackTrace(new PrintWriter(writer)); String trace = writer.toString(); String message; String sql; if (e instanceof JdbcSQLException) { JdbcSQLException j = (JdbcSQLException) e; message = j.getOriginalMessage(); sql = j.getSQL(); } else { message = e.getMessage(); sql = null; } transfer .writeInt(SessionRemote.STATUS_ERROR) .writeString(e.getSQLState()) .writeString(message) .writeString(sql) .writeInt(e.getErrorCode()) .writeString(trace) .flush(); } catch (Exception e2) { if (!transfer.isClosed()) { server.traceError(e2); } // if writing the error does not work, close the connection stop = true; } }
private void setAutoCommitSend(boolean autoCommit) { if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_8) { for (int i = 0, count = 0; i < transferList.size(); i++) { Transfer transfer = transferList.get(i); try { traceOperation("SESSION_SET_AUTOCOMMIT", autoCommit ? 1 : 0); transfer.writeInt(SessionRemote.SESSION_SET_AUTOCOMMIT).writeBoolean(autoCommit); done(transfer); } catch (IOException e) { removeServer(e, i--, ++count); } } } else { if (autoCommit) { if (autoCommitTrue == null) { autoCommitTrue = prepareCommand("SET AUTOCOMMIT TRUE", Integer.MAX_VALUE); } autoCommitTrue.executeUpdate(); } else { if (autoCommitFalse == null) { autoCommitFalse = prepareCommand("SET AUTOCOMMIT FALSE", Integer.MAX_VALUE); } autoCommitFalse.executeUpdate(); } } }
private void setParameters(Command command) throws IOException { int len = transfer.readInt(); ArrayList<? extends ParameterInterface> params = command.getParameters(); for (int i = 0; i < len; i++) { Parameter p = (Parameter) params.get(i); p.setValue(transfer.readValue()); } }
@Override public ArrayList<String> getClusterServers() { ArrayList<String> serverList = new ArrayList<String>(); for (int i = 0; i < transferList.size(); i++) { Transfer transfer = transferList.get(i); serverList.add( transfer.getSocket().getInetAddress().getHostAddress() + ":" + transfer.getSocket().getPort()); } return serverList; }
@Override public synchronized int readLob( long lobId, byte[] hmac, long offset, byte[] buff, int off, int length) { for (int i = 0, count = 0; i < transferList.size(); i++) { Transfer transfer = transferList.get(i); try { traceOperation("LOB_READ", (int) lobId); transfer.writeInt(SessionRemote.LOB_READ); transfer.writeLong(lobId); if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_12) { transfer.writeBytes(hmac); } transfer.writeLong(offset); transfer.writeInt(length); done(transfer); length = transfer.readInt(); if (length <= 0) { return length; } transfer.readBytes(buff, off, length); return length; } catch (IOException e) { removeServer(e, i--, ++count); } } return 1; }
private void sendRow(ResultInterface result) throws IOException { if (result.next()) { transfer.writeBoolean(true); Value[] v = result.currentRow(); for (int i = 0; i < result.getVisibleColumnCount(); i++) { if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_12) { transfer.writeValue(v[i]); } else { writeValue(v[i]); } } } else { transfer.writeBoolean(false); } }
/** Calls COMMIT if the session is in cluster mode. */ public void autoCommitIfCluster() { if (autoCommit && cluster) { // server side auto commit is off because of race conditions // (update set id=1 where id=0, but update set id=2 where id=0 is // faster) for (int i = 0, count = 0; i < transferList.size(); i++) { Transfer transfer = transferList.get(i); try { traceOperation("COMMAND_COMMIT", 0); transfer.writeInt(SessionRemote.COMMAND_COMMIT); done(transfer); } catch (IOException e) { removeServer(e, i--, ++count); } } } }
/** * Called to flush the output after data has been sent to the server and just before receiving * data. This method also reads the status code from the server and throws any exception the * server sent. * * @param transfer the transfer object * @throws DbException if the server sent an exception * @throws IOException if there is a communication problem between client and server */ public void done(Transfer transfer) throws IOException { transfer.flush(); int status = transfer.readInt(); if (status == STATUS_ERROR) { String sqlstate = transfer.readString(); String message = transfer.readString(); String sql = transfer.readString(); int errorCode = transfer.readInt(); String stackTrace = transfer.readString(); JdbcSQLException s = new JdbcSQLException(message, sql, sqlstate, errorCode, null, stackTrace); if (errorCode == ErrorCode.CONNECTION_BROKEN_1) { // allow re-connect IOException e = new IOException(s.toString(), s); throw e; } throw DbException.convert(s); } else if (status == STATUS_CLOSED) { transferList = null; } else if (status == STATUS_OK_STATE_CHANGED) { sessionStateChanged = true; } else if (status == STATUS_OK) { // ok } else { throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "unexpected status " + status); } }
@Override public boolean hasPendingTransaction() { if (clientVersion < Constants.TCP_PROTOCOL_VERSION_10) { return true; } for (int i = 0, count = 0; i < transferList.size(); i++) { Transfer transfer = transferList.get(i); try { traceOperation("SESSION_HAS_PENDING_TRANSACTION", 0); transfer.writeInt(SessionRemote.SESSION_HAS_PENDING_TRANSACTION); done(transfer); return transfer.readInt() != 0; } catch (IOException e) { removeServer(e, i--, ++count); } } return true; }
/** * Cancel the statement with the given id. * * @param id the statement id */ public void cancelStatement(int id) { for (Transfer transfer : transferList) { try { Transfer trans = transfer.openNewConnection(); trans.init(); trans.writeInt(clientVersion); trans.writeInt(clientVersion); trans.writeString(null); trans.writeString(null); trans.writeString(sessionId); trans.writeInt(SessionRemote.SESSION_CANCEL_STATEMENT); trans.writeInt(id); trans.close(); } catch (IOException e) { trace.debug(e, "could not cancel statement"); } } }
private void writeValue(Value v) throws IOException { if (v.getType() == Value.CLOB || v.getType() == Value.BLOB) { if (v instanceof ValueLobDb) { ValueLobDb lob = (ValueLobDb) v; if (lob.isStored()) { long id = lob.getLobId(); lobs.put(id, new CachedInputStream(null)); } } } transfer.writeValue(v); }
/** Close a connection. */ void close() { try { stop = true; closeSession(); } catch (Exception e) { server.traceError(e); } finally { transfer.close(); trace("Close"); server.remove(this); } }
// 总共18条命令,这里包含16条 // 下面这两条在run方法中特殊处理 // SESSION_CANCEL_STATEMENT // SESSION_CHECK_KEY // 分三种级别,从大到小: SESSION级、COMMAND级、RESULT级 private void process() throws IOException { int operation = transfer.readInt(); switch (operation) { case SessionRemote.SESSION_PREPARE_READ_PARAMS: case SessionRemote.SESSION_PREPARE: { int id = transfer.readInt(); String sql = transfer.readString(); int old = session.getModificationId(); Command command = session.prepareLocal(sql); boolean readonly = command.isReadOnly(); cache.addObject(id, command); boolean isQuery = command.isQuery(); ArrayList<? extends ParameterInterface> params = command.getParameters(); transfer .writeInt(getState(old)) .writeBoolean(isQuery) .writeBoolean(readonly) .writeInt(params.size()); if (operation == SessionRemote.SESSION_PREPARE_READ_PARAMS) { for (ParameterInterface p : params) { ParameterRemote.writeMetaData(transfer, p); } } transfer.flush(); break; } case SessionRemote.SESSION_CLOSE: { stop = true; closeSession(); transfer.writeInt(SessionRemote.STATUS_OK).flush(); close(); break; } case SessionRemote.COMMAND_COMMIT: { // 并不是通过org.h2.jdbc.JdbcConnection.commit()触发,此方法是通过发送COMMIT SQL触发 // 触发SessionRemote.COMMAND_COMMIT的是在集群环境下,通过org.h2.engine.SessionRemote.autoCommitIfCluster()触发 if (commit == null) { commit = session.prepareLocal("COMMIT"); } int old = session.getModificationId(); commit.executeUpdate(); transfer.writeInt(getState(old)).flush(); break; } case SessionRemote.COMMAND_GET_META_DATA: { int id = transfer.readInt(); int objectId = transfer.readInt(); Command command = (Command) cache.getObject(id, false); ResultInterface result = command.getMetaData(); cache.addObject(objectId, result); int columnCount = result.getVisibleColumnCount(); transfer.writeInt(SessionRemote.STATUS_OK).writeInt(columnCount).writeInt(0); for (int i = 0; i < columnCount; i++) { ResultColumn.writeColumn(transfer, result, i); } transfer.flush(); break; } case SessionRemote.COMMAND_EXECUTE_QUERY: { int id = transfer.readInt(); int objectId = transfer.readInt(); int maxRows = transfer.readInt(); int fetchSize = transfer.readInt(); Command command = (Command) cache.getObject(id, false); setParameters(command); int old = session.getModificationId(); ResultInterface result; synchronized (session) { result = command.executeQuery(maxRows, false); } cache.addObject(objectId, result); int columnCount = result.getVisibleColumnCount(); int state = getState(old); transfer.writeInt(state).writeInt(columnCount); int rowCount = result.getRowCount(); transfer.writeInt(rowCount); for (int i = 0; i < columnCount; i++) { ResultColumn.writeColumn(transfer, result, i); } int fetch = Math.min(rowCount, fetchSize); for (int i = 0; i < fetch; i++) { sendRow(result); } transfer.flush(); break; } case SessionRemote.COMMAND_EXECUTE_UPDATE: { int id = transfer.readInt(); Command command = (Command) cache.getObject(id, false); // if(command!=null)throw new Error(); setParameters(command); int old = session.getModificationId(); int updateCount; synchronized (session) { updateCount = command.executeUpdate(); } int status; if (session.isClosed()) { status = SessionRemote.STATUS_CLOSED; } else { status = getState(old); } transfer.writeInt(status).writeInt(updateCount).writeBoolean(session.getAutoCommit()); transfer.flush(); break; } case SessionRemote.COMMAND_CLOSE: { int id = transfer.readInt(); Command command = (Command) cache.getObject(id, true); if (command != null) { command.close(); cache.freeObject(id); } break; } case SessionRemote.RESULT_FETCH_ROWS: { int id = transfer.readInt(); int count = transfer.readInt(); ResultInterface result = (ResultInterface) cache.getObject(id, false); transfer.writeInt(SessionRemote.STATUS_OK); for (int i = 0; i < count; i++) { sendRow(result); } transfer.flush(); break; } case SessionRemote.RESULT_RESET: { int id = transfer.readInt(); ResultInterface result = (ResultInterface) cache.getObject(id, false); result.reset(); break; } case SessionRemote.RESULT_CLOSE: { int id = transfer.readInt(); ResultInterface result = (ResultInterface) cache.getObject(id, true); if (result != null) { result.close(); cache.freeObject(id); } break; } case SessionRemote.CHANGE_ID: { int oldId = transfer.readInt(); int newId = transfer.readInt(); Object obj = cache.getObject(oldId, false); cache.freeObject(oldId); cache.addObject(newId, obj); break; } case SessionRemote.SESSION_SET_ID: { sessionId = transfer.readString(); transfer.writeInt(SessionRemote.STATUS_OK).flush(); break; } case SessionRemote.SESSION_SET_AUTOCOMMIT: { boolean autoCommit = transfer.readBoolean(); session.setAutoCommit(autoCommit); transfer.writeInt(SessionRemote.STATUS_OK).flush(); break; } case SessionRemote.SESSION_HAS_PENDING_TRANSACTION: { transfer .writeInt(SessionRemote.STATUS_OK) .writeInt(session.hasPendingTransaction() ? 1 : 0) .flush(); break; } case SessionRemote.LOB_READ: { long lobId = transfer.readLong(); byte[] hmac; CachedInputStream in; boolean verifyMac; if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_11) { if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_12) { hmac = transfer.readBytes(); verifyMac = true; } else { hmac = null; verifyMac = false; } in = lobs.get(lobId); if (in == null && verifyMac) { in = new CachedInputStream(null); lobs.put(lobId, in); } } else { verifyMac = false; hmac = null; in = lobs.get(lobId); } long offset = transfer.readLong(); int length = transfer.readInt(); if (verifyMac) { transfer.verifyLobMac(hmac, lobId); } if (in == null) { throw DbException.get(ErrorCode.OBJECT_CLOSED); } if (in.getPos() != offset) { LobStorageInterface lobStorage = session.getDataHandler().getLobStorage(); InputStream lobIn = lobStorage.getInputStream(lobId, hmac, -1); in = new CachedInputStream(lobIn); lobs.put(lobId, in); lobIn.skip(offset); } // limit the buffer size length = Math.min(16 * Constants.IO_BUFFER_SIZE, length); byte[] buff = new byte[length]; length = IOUtils.readFully(in, buff, 0, length); transfer.writeInt(SessionRemote.STATUS_OK); transfer.writeInt(length); transfer.writeBytes(buff, 0, length); transfer.flush(); break; } default: trace("Unknown operation: " + operation); closeSession(); close(); } }
TcpServerThread(Socket socket, TcpServer server, int id) { this.server = server; this.threadId = id; transfer = new Transfer(null); transfer.setSocket(socket); }
@Override public void run() { try { transfer.init(); trace("Connect"); // TODO server: should support a list of allowed databases // and a list of allowed clients try { // 如果没有加-tcpAllowOthers参数,那么只接受本地连接 if (!server.allow(transfer.getSocket())) { throw DbException.get(ErrorCode.REMOTE_CONNECTION_NOT_ALLOWED); } int minClientVersion = transfer.readInt(); if (minClientVersion < Constants.TCP_PROTOCOL_VERSION_6) { throw DbException.get( ErrorCode.DRIVER_VERSION_ERROR_2, "" + clientVersion, "" + Constants.TCP_PROTOCOL_VERSION_6); } else if (minClientVersion > Constants.TCP_PROTOCOL_VERSION_12) { throw DbException.get( ErrorCode.DRIVER_VERSION_ERROR_2, "" + clientVersion, "" + Constants.TCP_PROTOCOL_VERSION_12); } int maxClientVersion = transfer.readInt(); if (maxClientVersion >= Constants.TCP_PROTOCOL_VERSION_12) { clientVersion = Constants.TCP_PROTOCOL_VERSION_12; } else { clientVersion = minClientVersion; } transfer.setVersion(clientVersion); String db = transfer.readString(); String originalURL = transfer.readString(); if (db == null && originalURL == null) { String targetSessionId = transfer.readString(); int command = transfer.readInt(); stop = true; if (command == SessionRemote.SESSION_CANCEL_STATEMENT) { // cancel a running statement int statementId = transfer.readInt(); server.cancelStatement(targetSessionId, statementId); } else if (command == SessionRemote.SESSION_CHECK_KEY) { // check if this is the correct server db = server.checkKeyAndGetDatabaseName(targetSessionId); if (!targetSessionId.equals(db)) { transfer.writeInt(SessionRemote.STATUS_OK); } else { transfer.writeInt(SessionRemote.STATUS_ERROR); } } } // 启动TcpServer时加"-baseDir"或者像这样System.setProperty("h2.baseDir", "E:\\H2\\baseDir") String baseDir = server.getBaseDir(); if (baseDir == null) { baseDir = SysProperties.getBaseDir(); } // 例如启动TcpServer时,指定了"-key mydb mydatabase", // 如果db变量是mydb,那么实际上就是mydatabase,相当于做一次映射 // 如果db变量不是mydb,那么抛错: org.h2.jdbc.JdbcSQLException: Wrong user name or password [28000-170] db = server.checkKeyAndGetDatabaseName(db); ConnectionInfo ci = new ConnectionInfo(db); ci.setOriginalURL(originalURL); ci.setUserName(transfer.readString()); // password参数的值已经转换成userPasswordHash和filePasswordHash了, // 不能由userPasswordHash和filePasswordHash得到原始的password ci.setUserPasswordHash(transfer.readBytes()); ci.setFilePasswordHash(transfer.readBytes()); // 只有指定"CIPHER"参数时filePasswordHash才是非null的 int len = transfer.readInt(); for (int i = 0; i < len; i++) { ci.setProperty(transfer.readString(), transfer.readString()); } // override client's requested properties with server settings if (baseDir != null) { ci.setBaseDir(baseDir); } if (server.getIfExists()) { // 启动TcpServer时加"-ifExists",限制只有数据库存在时客户端才能连接,也就是不允许在客户端创建数据库 ci.setProperty("IFEXISTS", "TRUE"); } session = Engine.getInstance().createSession(ci); transfer.setSession(session); transfer.writeInt(SessionRemote.STATUS_OK); transfer.writeInt(clientVersion); transfer.flush(); // 每建立一个新的Session对象时,把它保存到内存数据库management_db_9092的SESSIONS表 server.addConnection(threadId, originalURL, ci.getUserName()); trace("Connected"); } catch (Throwable e) { sendError(e); stop = true; } while (!stop) { try { process(); } catch (Throwable e) { sendError(e); } } trace("Disconnect"); } catch (Throwable e) { server.traceError(e); } finally { close(); } }
private Transfer initTransfer(ConnectionInfo ci, String db, String server) throws IOException { Socket socket = NetUtils.createSocket(server, Constants.DEFAULT_TCP_PORT, ci.isSSL()); Transfer trans = new Transfer(this); trans.setSocket(socket); trans.setSSL(ci.isSSL()); trans.init(); trans.writeInt(Constants.TCP_PROTOCOL_VERSION_6); trans.writeInt(Constants.TCP_PROTOCOL_VERSION_15); trans.writeString(db); trans.writeString(ci.getOriginalURL()); trans.writeString(ci.getUserName()); trans.writeBytes(ci.getUserPasswordHash()); trans.writeBytes(ci.getFilePasswordHash()); String[] keys = ci.getKeys(); trans.writeInt(keys.length); for (String key : keys) { trans.writeString(key).writeString(ci.getProperty(key)); } try { done(trans); clientVersion = trans.readInt(); trans.setVersion(clientVersion); if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_14) { if (ci.getFileEncryptionKey() != null) { trans.writeBytes(ci.getFileEncryptionKey()); } } trans.writeInt(SessionRemote.SESSION_SET_ID); trans.writeString(sessionId); done(trans); if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_15) { autoCommit = trans.readBoolean(); } else { autoCommit = true; } return trans; } catch (DbException e) { trans.close(); throw e; } }