/** INTERNAL */ public JdbcConnection(ConnectionInfo ci, boolean useBaseDir) throws SQLException { try { if (useBaseDir) { String baseDir = SysProperties.getBaseDir(); if (baseDir != null) { ci.setBaseDir(baseDir); } } checkJavaVersion(); // this will return an embedded or server connection session = new SessionRemote().createSession(ci); trace = session.getTrace(); int id = getNextId(TraceObject.CONNECTION); setTrace(trace, TraceObject.CONNECTION, id); this.user = ci.getUserName(); if (isInfoEnabled()) { trace.infoCode( "Connection " + getTraceObjectName() + " = DriverManager.getConnection(" + quote(ci.getOriginalURL()) + ", " + quote(user) + ", \"\");"); } this.url = ci.getURL(); openStackTrace = new Exception("Stack Trace"); } catch (Exception e) { throw logAndConvert(e); } }
@Override public ConnectionInfo clone() throws CloneNotSupportedException { ConnectionInfo clone = (ConnectionInfo) super.clone(); clone.prop = (Properties) prop.clone(); clone.filePasswordHash = Utils.cloneByteArray(filePasswordHash); clone.fileEncryptionKey = Utils.cloneByteArray(fileEncryptionKey); clone.userPasswordHash = Utils.cloneByteArray(userPasswordHash); return clone; }
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; } }
/** * Open a new (remote or embedded) session. * * @param openNew whether to open a new session in any case * @return the session */ public SessionInterface connectEmbeddedOrServer(boolean openNew) { ConnectionInfo ci = connectionInfo; if (ci.isRemote()) { connectServer(ci); return this; } // create the session using reflection, // so that the JDBC layer can be compiled without it boolean autoServerMode = Boolean.parseBoolean(ci.getProperty("AUTO_SERVER", "false")); ConnectionInfo backup = null; try { if (autoServerMode) { backup = ci.clone(); connectionInfo = ci.clone(); } if (openNew) { ci.setProperty("OPEN_NEW", "true"); } if (sessionFactory == null) { sessionFactory = (SessionFactory) Class.forName("org.h2.engine.Engine").getMethod("getInstance").invoke(null); } return sessionFactory.createSession(ci); } catch (Exception re) { DbException e = DbException.convert(re); if (e.getErrorCode() == ErrorCode.DATABASE_ALREADY_OPEN_1) { if (autoServerMode) { String serverKey = ((JdbcSQLException) e.getSQLException()).getSQL(); if (serverKey != null) { backup.setServerKey(serverKey); // OPEN_NEW must be removed now, otherwise // opening a session with AUTO_SERVER fails // if another connection is already open backup.removeProperty("OPEN_NEW", null); connectServer(backup); return this; } } } throw e; } }
@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 void connectServer(ConnectionInfo ci) { String name = ci.getName(); if (name.startsWith("//")) { name = name.substring("//".length()); } int idx = name.indexOf('/'); if (idx < 0) { throw ci.getFormatException(); } databaseName = name.substring(idx + 1); String server = name.substring(0, idx); traceSystem = new TraceSystem(null); String traceLevelFile = ci.getProperty(SetTypes.TRACE_LEVEL_FILE, null); if (traceLevelFile != null) { int level = Integer.parseInt(traceLevelFile); String prefix = getFilePrefix(SysProperties.CLIENT_TRACE_DIRECTORY); try { traceSystem.setLevelFile(level); if (level > 0 && level < 4) { String file = FileUtils.createTempFile(prefix, Constants.SUFFIX_TRACE_FILE, false, false); traceSystem.setFileName(file); } } catch (IOException e) { throw DbException.convertIOException(e, prefix); } } String traceLevelSystemOut = ci.getProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT, null); if (traceLevelSystemOut != null) { int level = Integer.parseInt(traceLevelSystemOut); traceSystem.setLevelSystemOut(level); } trace = traceSystem.getTrace(Trace.JDBC); String serverList = null; if (server.indexOf(',') >= 0) { serverList = StringUtils.quoteStringSQL(server); ci.setProperty("CLUSTER", Constants.CLUSTERING_ENABLED); } autoReconnect = Boolean.parseBoolean(ci.getProperty("AUTO_RECONNECT", "false")); // AUTO_SERVER implies AUTO_RECONNECT boolean autoServer = Boolean.parseBoolean(ci.getProperty("AUTO_SERVER", "false")); if (autoServer && serverList != null) { throw DbException.getUnsupportedException("autoServer && serverList != null"); } autoReconnect |= autoServer; if (autoReconnect) { String className = ci.getProperty("DATABASE_EVENT_LISTENER"); if (className != null) { className = StringUtils.trim(className, true, true, "'"); try { eventListener = (DatabaseEventListener) JdbcUtils.loadUserClass(className).newInstance(); } catch (Throwable e) { throw DbException.convert(e); } } } cipher = ci.getProperty("CIPHER"); if (cipher != null) { fileEncryptionKey = MathUtils.secureRandomBytes(32); } String[] servers = StringUtils.arraySplit(server, ',', true); int len = servers.length; transferList.clear(); sessionId = StringUtils.convertBytesToHex(MathUtils.secureRandomBytes(32)); // TODO cluster: support more than 2 connections boolean switchOffCluster = false; try { for (int i = 0; i < len; i++) { String s = servers[i]; try { Transfer trans = initTransfer(ci, databaseName, s); transferList.add(trans); } catch (IOException e) { if (len == 1) { throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, e, e + ": " + s); } switchOffCluster = true; } } checkClosed(); if (switchOffCluster) { switchOffCluster(); } checkClusterDisableAutoCommit(serverList); } catch (DbException e) { traceSystem.close(); throw e; } }