diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0535ccd7dc..344f0cb430 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -11,12 +11,6 @@ jobs: uses: actions/setup-java@v1 with: java-version: 1.8 - - name: Test - run: | - cd h2 - echo $JAVA_OPTS - export JAVA_OPTS=-Xmx512m - ./build.sh jar testCI java-11: runs-on: ubuntu-latest steps: @@ -25,9 +19,3 @@ jobs: uses: actions/setup-java@v1 with: java-version: 11 - - name: Test - run: | - cd h2 - echo $JAVA_OPTS - export JAVA_OPTS=-Xmx512m - ./build.sh jar testCI diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml new file mode 100644 index 0000000000..dab69fef79 --- /dev/null +++ b/.github/workflows/maven-publish.yml @@ -0,0 +1,34 @@ +# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created +# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path + +name: Maven Package + +on: + release: + types: [created] + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + server-id: github # Value of the distributionManagement/repository/id field of the pom.xml + settings-path: ${{ github.workspace }} # location for the settings.xml file + + - name: Build with Maven + run: mvn -B package --file pom.xml + + - name: Publish to GitHub Packages Apache Maven + run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/h2/src/main/org/h2/server/TcpServer.java b/h2/src/main/org/h2/server/TcpServer.java index ac6c5fee76..6b44455dc9 100644 --- a/h2/src/main/org/h2/server/TcpServer.java +++ b/h2/src/main/org/h2/server/TcpServer.java @@ -173,7 +173,7 @@ private synchronized void stopManagementDb() { } @Override - public void init(String... args) { + public void init(String... args) { // Server 创建Service后初始化参数 port = Constants.DEFAULT_TCP_PORT; for (int i = 0; args != null && i < args.length; i++) { String a = args[i]; diff --git a/h2/src/main/org/h2/server/web/WebServer.java b/h2/src/main/org/h2/server/web/WebServer.java index bc7fd83c39..ba0d6ad011 100644 --- a/h2/src/main/org/h2/server/web/WebServer.java +++ b/h2/src/main/org/h2/server/web/WebServer.java @@ -1,933 +1,933 @@ -/* - * Copyright 2004-2021 H2 Group. Multiple-Licensed under the MPL 2.0, - * and the EPL 1.0 (https://h2database.com/html/license.html). - * Initial Developer: H2 Group - */ -package org.h2.server.web; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.ServerSocket; -import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.sql.Connection; -import java.sql.SQLException; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Properties; -import java.util.Set; - -import org.h2.engine.Constants; -import org.h2.engine.SysProperties; -import org.h2.message.DbException; -import org.h2.security.SHA256; -import org.h2.server.Service; -import org.h2.server.ShutdownHandler; -import org.h2.store.fs.FileUtils; -import org.h2.util.JdbcUtils; -import org.h2.util.MathUtils; -import org.h2.util.NetUtils; -import org.h2.util.NetworkConnectionInfo; -import org.h2.util.SortedProperties; -import org.h2.util.StringUtils; -import org.h2.util.Tool; -import org.h2.util.Utils; - -/** - * The web server is a simple standalone HTTP server that implements the H2 - * Console application. It is not optimized for performance. - */ -public class WebServer implements Service { - - static final String[][] LANGUAGES = { - { "cs", "\u010ce\u0161tina" }, - { "de", "Deutsch" }, - { "en", "English" }, - { "es", "Espa\u00f1ol" }, - { "fr", "Fran\u00e7ais" }, - { "hi", "Hindi \u0939\u093f\u0902\u0926\u0940" }, - { "hu", "Magyar"}, - { "ko", "\ud55c\uad6d\uc5b4"}, - { "in", "Indonesia"}, - { "it", "Italiano"}, - { "ja", "\u65e5\u672c\u8a9e"}, - { "nl", "Nederlands"}, - { "pl", "Polski"}, - { "pt_BR", "Portugu\u00eas (Brasil)"}, - { "pt_PT", "Portugu\u00eas (Europeu)"}, - { "ru", "\u0440\u0443\u0441\u0441\u043a\u0438\u0439"}, - { "sk", "Slovensky"}, - { "tr", "T\u00fcrk\u00e7e"}, - { "uk", "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"}, - { "zh_CN", "\u4e2d\u6587 (\u7b80\u4f53)"}, - { "zh_TW", "\u4e2d\u6587 (\u7e41\u9ad4)"}, - }; - - private static final String COMMAND_HISTORY = "commandHistory"; - - private static final String DEFAULT_LANGUAGE = "en"; - - private static final String[] GENERIC = { - "Generic JNDI Data Source|javax.naming.InitialContext|" + - "java:comp/env/jdbc/Test|sa", - "Generic Teradata|com.teradata.jdbc.TeraDriver|" + - "jdbc:teradata://whomooz/|", - "Generic Snowflake|com.snowflake.client.jdbc.SnowflakeDriver|" + - "jdbc:snowflake://accountName.snowflakecomputing.com|", - "Generic Redshift|com.amazon.redshift.jdbc42.Driver|" + - "jdbc:redshift://endpoint:5439/database|", - "Generic Impala|org.cloudera.impala.jdbc41.Driver|" + - "jdbc:impala://clustername:21050/default|", - "Generic Hive 2|org.apache.hive.jdbc.HiveDriver|" + - "jdbc:hive2://clustername:10000/default|", - "Generic Hive|org.apache.hadoop.hive.jdbc.HiveDriver|" + - "jdbc:hive://clustername:10000/default|", - "Generic Azure SQL|com.microsoft.sqlserver.jdbc.SQLServerDriver|" + - "jdbc:sqlserver://name.database.windows.net:1433|", - "Generic Firebird Server|org.firebirdsql.jdbc.FBDriver|" + - "jdbc:firebirdsql:localhost:c:/temp/firebird/test|sysdba", - "Generic SQLite|org.sqlite.JDBC|" + - "jdbc:sqlite:test|sa", - "Generic DB2|com.ibm.db2.jcc.DB2Driver|" + - "jdbc:db2://localhost/test|" , - "Generic Oracle|oracle.jdbc.driver.OracleDriver|" + - "jdbc:oracle:thin:@localhost:1521:XE|sa" , - "Generic MS SQL Server 2000|com.microsoft.jdbc.sqlserver.SQLServerDriver|" + - "jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=sqlexpress|sa", - "Generic MS SQL Server 2005|com.microsoft.sqlserver.jdbc.SQLServerDriver|" + - "jdbc:sqlserver://localhost;DatabaseName=test|sa", - "Generic PostgreSQL|org.postgresql.Driver|" + - "jdbc:postgresql:test|" , - "Generic MySQL|com.mysql.cj.jdbc.Driver|" + - "jdbc:mysql://localhost:3306/test|" , - "Generic MariaDB|org.mariadb.jdbc.Driver|" + - "jdbc:mariadb://localhost:3306/test|" , - "Generic HSQLDB|org.hsqldb.jdbcDriver|" + - "jdbc:hsqldb:test;hsqldb.default_table_type=cached|sa" , - "Generic Derby (Server)|org.apache.derby.client.ClientAutoloadedDriver|" + - "jdbc:derby://localhost:1527/test;create=true|sa", - "Generic Derby (Embedded)|org.apache.derby.iapi.jdbc.AutoloadedDriver|" + - "jdbc:derby:test;create=true|sa", - "Generic H2 (Server)|org.h2.Driver|" + - "jdbc:h2:tcp://localhost/~/test|sa", - // this will be listed on top for new installations - "Generic H2 (Embedded)|org.h2.Driver|" + - "jdbc:h2:~/test|sa", - }; - - private static int ticker; - - /** - * The session timeout (the default is 30 minutes). - */ - private static final long SESSION_TIMEOUT = SysProperties.CONSOLE_TIMEOUT; - -// public static void main(String... args) throws IOException { -// String s = IOUtils.readStringAndClose(new java.io.FileReader( -// // "src/main/org/h2/server/web/res/_text_cs.prop"), -1); -// "src/main/org/h2/res/_messages_cs.prop"), -1); -// System.out.println(StringUtils.javaEncode("...")); -// String[] list = Locale.getISOLanguages(); -// for (int i = 0; i < list.length; i++) { -// System.out.print(list[i] + " "); -// } -// System.out.println(); -// String l = "de"; -// String lang = new java.util.Locale(l). -// getDisplayLanguage(new java.util.Locale(l)); -// System.out.println(new java.util.Locale(l).getDisplayLanguage()); -// System.out.println(lang); -// java.util.Locale.CHINESE.getDisplayLanguage(java.util.Locale.CHINESE); -// for (int i = 0; i < lang.length(); i++) { -// System.out.println(Integer.toHexString(lang.charAt(i)) + " "); -// } -// } - - // private URLClassLoader urlClassLoader; - private int port; - private boolean allowOthers; - private boolean isDaemon; - private final Set running = - Collections.synchronizedSet(new HashSet()); - private boolean ssl; - private byte[] adminPassword; - private final HashMap connInfoMap = new HashMap<>(); - - private long lastTimeoutCheck; - private final HashMap sessions = new HashMap<>(); - private final HashSet languages = new HashSet<>(); - private String startDateTime; - private ServerSocket serverSocket; - private String url; - private ShutdownHandler shutdownHandler; - private Thread listenerThread; - private boolean ifExists = true; - private String key; - private boolean allowSecureCreation; - private boolean trace; - private TranslateThread translateThread; - private boolean allowChunked = true; - private String serverPropertiesDir = Constants.SERVER_PROPERTIES_DIR; - // null means the history is not allowed to be stored - private String commandHistoryString; - - /** - * Read the given file from the file system or from the resources. - * - * @param file the file name - * @return the data - */ - byte[] getFile(String file) throws IOException { - trace("getFile <" + file + ">"); - byte[] data = Utils.getResource("/org/h2/server/web/res/" + file); - if (data == null) { - trace(" null"); - } else { - trace(" size=" + data.length); - } - return data; - } - - /** - * Remove this web thread from the set of running threads. - * - * @param t the thread to remove - */ - synchronized void remove(WebThread t) { - running.remove(t); - } - - private static String generateSessionId() { - byte[] buff = MathUtils.secureRandomBytes(16); - return StringUtils.convertBytesToHex(buff); - } - - /** - * Get the web session object for the given session id. - * - * @param sessionId the session id - * @return the web session or null - */ - WebSession getSession(String sessionId) { - long now = System.currentTimeMillis(); - if (lastTimeoutCheck + SESSION_TIMEOUT < now) { - for (String id : new ArrayList<>(sessions.keySet())) { - WebSession session = sessions.get(id); - if (session.lastAccess + SESSION_TIMEOUT < now) { - trace("timeout for " + id); - sessions.remove(id); - } - } - lastTimeoutCheck = now; - } - WebSession session = sessions.get(sessionId); - if (session != null) { - session.lastAccess = System.currentTimeMillis(); - } - return session; - } - - /** - * Create a new web session id and object. - * - * @param hostAddr the host address - * @return the web session object - */ - WebSession createNewSession(String hostAddr) { - String newId; - do { - newId = generateSessionId(); - } while (sessions.get(newId) != null); - WebSession session = new WebSession(this); - session.lastAccess = System.currentTimeMillis(); - session.put("sessionId", newId); - session.put("ip", hostAddr); - session.put("language", DEFAULT_LANGUAGE); - session.put("frame-border", "0"); - session.put("frameset-border", "4"); - sessions.put(newId, session); - // always read the english translation, - // so that untranslated text appears at least in english - readTranslations(session, DEFAULT_LANGUAGE); - return getSession(newId); - } - - String getStartDateTime() { - if (startDateTime == null) { - startDateTime = DateTimeFormatter.ofPattern("EEE, d MMM yyyy HH:mm:ss z", Locale.ENGLISH) - .format(ZonedDateTime.now(ZoneId.of("UTC"))); - } - return startDateTime; - } - - /** - * Returns the key for privileged connections. - * - * @return key key, or null - */ - String getKey() { - return key; - } - - /** - * Sets the key for privileged connections. - * - * @param key key, or null - */ - public void setKey(String key) { - if (!allowOthers) { - this.key = key; - } - } - - /** - * @param allowSecureCreation - * whether creation of databases using the key should be allowed - */ - public void setAllowSecureCreation(boolean allowSecureCreation) { - if (!allowOthers) { - this.allowSecureCreation = allowSecureCreation; - } - } - - @Override - public void init(String... args) { - // set the serverPropertiesDir, because it's used in loadProperties() - for (int i = 0; args != null && i < args.length; i++) { - if ("-properties".equals(args[i])) { - serverPropertiesDir = args[++i]; - } - } - Properties prop = loadProperties(); - port = SortedProperties.getIntProperty(prop, - "webPort", Constants.DEFAULT_HTTP_PORT); - ssl = SortedProperties.getBooleanProperty(prop, - "webSSL", false); - allowOthers = SortedProperties.getBooleanProperty(prop, - "webAllowOthers", false); - setAdminPassword(SortedProperties.getStringProperty(prop, "webAdminPassword", null)); - commandHistoryString = prop.getProperty(COMMAND_HISTORY); - for (int i = 0; args != null && i < args.length; i++) { - String a = args[i]; - if (Tool.isOption(a, "-webPort")) { - port = Integer.decode(args[++i]); - } else if (Tool.isOption(a, "-webSSL")) { - ssl = true; - } else if (Tool.isOption(a, "-webAllowOthers")) { - allowOthers = true; - } else if (Tool.isOption(a, "-webDaemon")) { - isDaemon = true; - } else if (Tool.isOption(a, "-baseDir")) { - String baseDir = args[++i]; - SysProperties.setBaseDir(baseDir); - } else if (Tool.isOption(a, "-ifExists")) { - ifExists = true; - } else if (Tool.isOption(a, "-ifNotExists")) { - ifExists = false; - } else if (Tool.isOption(a, "-webAdminPassword")) { - setAdminPassword(args[++i]); - } else if (Tool.isOption(a, "-properties")) { - // already set - i++; - } else if (Tool.isOption(a, "-trace")) { - trace = true; - } - } -// if (driverList != null) { -// try { -// String[] drivers = -// StringUtils.arraySplit(driverList, ',', false); -// URL[] urls = new URL[drivers.length]; -// for(int i=0; i entry : text.entrySet()) { - String value = (String) entry.getValue(); - if (value.startsWith("#")) { - entry.setValue(value.substring(1)); - } - } - } catch (IOException e) { - DbException.traceThrowable(e); - } - session.put("text", new HashMap<>(text)); - } - - ArrayList> getSessions() { - ArrayList> list = new ArrayList<>(sessions.size()); - for (WebSession s : sessions.values()) { - list.add(s.getInfo()); - } - return list; - } - - @Override - public String getType() { - return "Web Console"; - } - - @Override - public String getName() { - return "H2 Console Server"; - } - - void setAllowOthers(boolean b) { - if (b) { - key = null; - } - allowOthers = b; - } - - @Override - public boolean getAllowOthers() { - return allowOthers; - } - - void setSSL(boolean b) { - ssl = b; - } - - void setPort(int port) { - this.port = port; - } - - boolean getSSL() { - return ssl; - } - - @Override - public int getPort() { - return port; - } - - public boolean isCommandHistoryAllowed() { - return commandHistoryString != null; - } - - public void setCommandHistoryAllowed(boolean allowed) { - if (allowed) { - if (commandHistoryString == null) { - commandHistoryString = ""; - } - } else { - commandHistoryString = null; - } - } - - public ArrayList getCommandHistoryList() { - ArrayList result = new ArrayList<>(); - if (commandHistoryString == null) { - return result; - } - - // Split the commandHistoryString on non-escaped semicolons - // and unescape it. - StringBuilder sb = new StringBuilder(); - for (int end = 0;; end++) { - if (end == commandHistoryString.length() || - commandHistoryString.charAt(end) == ';') { - if (sb.length() > 0) { - result.add(sb.toString()); - sb.delete(0, sb.length()); - } - if (end == commandHistoryString.length()) { - break; - } - } else if (commandHistoryString.charAt(end) == '\\' && - end < commandHistoryString.length() - 1) { - sb.append(commandHistoryString.charAt(++end)); - } else { - sb.append(commandHistoryString.charAt(end)); - } - } - return result; - } - - /** - * Save the command history to the properties file. - * - * @param commandHistory the history - */ - public void saveCommandHistoryList(ArrayList commandHistory) { - StringBuilder sb = new StringBuilder(); - for (String s : commandHistory) { - if (sb.length() > 0) { - sb.append(';'); - } - sb.append(s.replace("\\", "\\\\").replace(";", "\\;")); - } - commandHistoryString = sb.toString(); - saveProperties(null); - } - - /** - * Get the connection information for this setting. - * - * @param name the setting name - * @return the connection information - */ - ConnectionInfo getSetting(String name) { - return connInfoMap.get(name); - } - - /** - * Update a connection information setting. - * - * @param info the connection information - */ - void updateSetting(ConnectionInfo info) { - connInfoMap.put(info.name, info); - info.lastAccess = ticker++; - } - - /** - * Remove a connection information setting from the list - * - * @param name the setting to remove - */ - void removeSetting(String name) { - connInfoMap.remove(name); - } - - private Properties loadProperties() { - try { - if ("null".equals(serverPropertiesDir)) { - return new Properties(); - } - return SortedProperties.loadProperties( - serverPropertiesDir + "/" + Constants.SERVER_PROPERTIES_NAME); - } catch (Exception e) { - DbException.traceThrowable(e); - return new Properties(); - } - } - - /** - * Get the list of connection information setting names. - * - * @return the connection info names - */ - String[] getSettingNames() { - ArrayList list = getSettings(); - String[] names = new String[list.size()]; - for (int i = 0; i < list.size(); i++) { - names[i] = list.get(i).name; - } - return names; - } - - /** - * Get the list of connection info objects. - * - * @return the list - */ - synchronized ArrayList getSettings() { - ArrayList settings = new ArrayList<>(); - if (connInfoMap.size() == 0) { - Properties prop = loadProperties(); - if (prop.size() == 0) { - for (String gen : GENERIC) { - ConnectionInfo info = new ConnectionInfo(gen); - settings.add(info); - updateSetting(info); - } - } else { - for (int i = 0;; i++) { - String data = prop.getProperty(Integer.toString(i)); - if (data == null) { - break; - } - ConnectionInfo info = new ConnectionInfo(data); - settings.add(info); - updateSetting(info); - } - } - } else { - settings.addAll(connInfoMap.values()); - } - Collections.sort(settings); - return settings; - } - - /** - * Save the settings to the properties file. - * - * @param prop null or the properties webPort, webAllowOthers, and webSSL - */ - synchronized void saveProperties(Properties prop) { - try { - if (prop == null) { - Properties old = loadProperties(); - prop = new SortedProperties(); - prop.setProperty("webPort", - Integer.toString(SortedProperties.getIntProperty(old, "webPort", port))); - prop.setProperty("webAllowOthers", - Boolean.toString(SortedProperties.getBooleanProperty(old, "webAllowOthers", allowOthers))); - prop.setProperty("webSSL", - Boolean.toString(SortedProperties.getBooleanProperty(old, "webSSL", ssl))); - if (adminPassword != null) { - prop.setProperty("webAdminPassword", StringUtils.convertBytesToHex(adminPassword)); - } - if (commandHistoryString != null) { - prop.setProperty(COMMAND_HISTORY, commandHistoryString); - } - } - ArrayList settings = getSettings(); - int len = settings.size(); - for (int i = 0; i < len; i++) { - ConnectionInfo info = settings.get(i); - if (info != null) { - prop.setProperty(Integer.toString(len - i - 1), info.getString()); - } - } - if (!"null".equals(serverPropertiesDir)) { - OutputStream out = FileUtils.newOutputStream( - serverPropertiesDir + "/" + Constants.SERVER_PROPERTIES_NAME, false); - prop.store(out, "H2 Server Properties"); - out.close(); - } - } catch (Exception e) { - DbException.traceThrowable(e); - } - } - - /** - * Open a database connection. - * - * @param driver the driver class name - * @param databaseUrl the database URL - * @param user the user name - * @param password the password - * @param userKey the key of privileged user - * @param networkConnectionInfo the network connection information - * @return the database connection - */ - Connection getConnection(String driver, String databaseUrl, String user, - String password, String userKey, NetworkConnectionInfo networkConnectionInfo) throws SQLException { - driver = driver.trim(); - databaseUrl = databaseUrl.trim(); - if (databaseUrl.startsWith("jdbc:h2:")) { - if (!allowSecureCreation || key == null || !key.equals(userKey)) { - if (ifExists) { - databaseUrl += ";FORBID_CREATION=TRUE"; - } - } - } - // do not trim the password, otherwise an - // encrypted H2 database with empty user password doesn't work - return JdbcUtils.getConnection(driver, databaseUrl, user.trim(), password, networkConnectionInfo); - } - - /** - * Shut down the web server. - */ - void shutdown() { - if (shutdownHandler != null) { - shutdownHandler.shutdown(); - } - } - - public void setShutdownHandler(ShutdownHandler shutdownHandler) { - this.shutdownHandler = shutdownHandler; - } - - /** - * Create a session with a given connection. - * - * @param conn the connection - * @return the URL of the web site to access this connection - */ - public String addSession(Connection conn) throws SQLException { - WebSession session = createNewSession("local"); - session.setShutdownServerOnDisconnect(); - session.setConnection(conn); - session.put("url", conn.getMetaData().getURL()); - String s = (String) session.get("sessionId"); - return url + "/frame.jsp?jsessionid=" + s; - } - - /** - * The translate thread reads and writes the file translation.properties - * once a second. - */ - private class TranslateThread extends Thread { - - private final Path file = Paths.get("translation.properties"); - private final Map translation; - private volatile boolean stopNow; - - TranslateThread(Map translation) { - this.translation = translation; - } - - public String getFileName() { - return file.toAbsolutePath().toString(); - } - - public void stopNow() { - this.stopNow = true; - try { - join(); - } catch (InterruptedException e) { - // ignore - } - } - - @Override - public void run() { - while (!stopNow) { - try { - SortedProperties sp = new SortedProperties(); - if (Files.exists(file)) { - InputStream in = Files.newInputStream(file); - sp.load(in); - translation.putAll(sp); - } else { - OutputStream out = Files.newOutputStream(file); - sp.putAll(translation); - sp.store(out, "Translation"); - } - Thread.sleep(1000); - } catch (Exception e) { - traceError(e); - } - } - } - - } - - /** - * Start the translation thread that reads the file once a second. - * - * @param translation the translation map - * @return the name of the file to translate - */ - String startTranslate(Map translation) { - if (translateThread != null) { - translateThread.stopNow(); - } - translateThread = new TranslateThread(translation); - translateThread.setDaemon(true); - translateThread.start(); - return translateThread.getFileName(); - } - - @Override - public boolean isDaemon() { - return isDaemon; - } - - void setAllowChunked(boolean allowChunked) { - this.allowChunked = allowChunked; - } - - boolean getAllowChunked() { - return allowChunked; - } - - byte[] getAdminPassword() { - return adminPassword; - } - - void setAdminPassword(String password) { - if (password == null || password.isEmpty()) { - adminPassword = null; - return; - } - if (password.length() == 128) { - try { - adminPassword = StringUtils.convertHexToBytes(password); - return; - } catch (Exception ex) {} - } - byte[] salt = MathUtils.secureRandomBytes(32); - byte[] hash = SHA256.getHashWithSalt(password.getBytes(StandardCharsets.UTF_8), salt); - byte[] total = Arrays.copyOf(salt, 64); - System.arraycopy(hash, 0, total, 32, 32); - adminPassword = total; - } - - /** - * Check the admin password. - * - * @param password the password to test - * @return true if admin password not configure, or admin password correct - */ - boolean checkAdminPassword(String password) { - if (adminPassword == null) { - return false; - } - byte[] salt = Arrays.copyOf(adminPassword, 32); - byte[] hash = new byte[32]; - System.arraycopy(adminPassword, 32, hash, 0, 32); - return Utils.compareSecure(hash, SHA256.getHashWithSalt(password.getBytes(StandardCharsets.UTF_8), salt)); - } - -} +/* + * Copyright 2004-2021 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.h2.server.web; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.SQLException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; + +import org.h2.engine.Constants; +import org.h2.engine.SysProperties; +import org.h2.message.DbException; +import org.h2.security.SHA256; +import org.h2.server.Service; +import org.h2.server.ShutdownHandler; +import org.h2.store.fs.FileUtils; +import org.h2.util.JdbcUtils; +import org.h2.util.MathUtils; +import org.h2.util.NetUtils; +import org.h2.util.NetworkConnectionInfo; +import org.h2.util.SortedProperties; +import org.h2.util.StringUtils; +import org.h2.util.Tool; +import org.h2.util.Utils; + +/** + * The web server is a simple standalone HTTP server that implements the H2 + * Console application. It is not optimized for performance. + */ +public class WebServer implements Service { + + static final String[][] LANGUAGES = { + { "cs", "\u010ce\u0161tina" }, + { "de", "Deutsch" }, + { "en", "English" }, + { "es", "Espa\u00f1ol" }, + { "fr", "Fran\u00e7ais" }, + { "hi", "Hindi \u0939\u093f\u0902\u0926\u0940" }, + { "hu", "Magyar"}, + { "ko", "\ud55c\uad6d\uc5b4"}, + { "in", "Indonesia"}, + { "it", "Italiano"}, + { "ja", "\u65e5\u672c\u8a9e"}, + { "nl", "Nederlands"}, + { "pl", "Polski"}, + { "pt_BR", "Portugu\u00eas (Brasil)"}, + { "pt_PT", "Portugu\u00eas (Europeu)"}, + { "ru", "\u0440\u0443\u0441\u0441\u043a\u0438\u0439"}, + { "sk", "Slovensky"}, + { "tr", "T\u00fcrk\u00e7e"}, + { "uk", "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"}, + { "zh_CN", "\u4e2d\u6587 (\u7b80\u4f53)"}, + { "zh_TW", "\u4e2d\u6587 (\u7e41\u9ad4)"}, + }; + + private static final String COMMAND_HISTORY = "commandHistory"; + + private static final String DEFAULT_LANGUAGE = "en"; + + private static final String[] GENERIC = { + "Generic JNDI Data Source|javax.naming.InitialContext|" + + "java:comp/env/jdbc/Test|sa", + "Generic Teradata|com.teradata.jdbc.TeraDriver|" + + "jdbc:teradata://whomooz/|", + "Generic Snowflake|com.snowflake.client.jdbc.SnowflakeDriver|" + + "jdbc:snowflake://accountName.snowflakecomputing.com|", + "Generic Redshift|com.amazon.redshift.jdbc42.Driver|" + + "jdbc:redshift://endpoint:5439/database|", + "Generic Impala|org.cloudera.impala.jdbc41.Driver|" + + "jdbc:impala://clustername:21050/default|", + "Generic Hive 2|org.apache.hive.jdbc.HiveDriver|" + + "jdbc:hive2://clustername:10000/default|", + "Generic Hive|org.apache.hadoop.hive.jdbc.HiveDriver|" + + "jdbc:hive://clustername:10000/default|", + "Generic Azure SQL|com.microsoft.sqlserver.jdbc.SQLServerDriver|" + + "jdbc:sqlserver://name.database.windows.net:1433|", + "Generic Firebird Server|org.firebirdsql.jdbc.FBDriver|" + + "jdbc:firebirdsql:localhost:c:/temp/firebird/test|sysdba", + "Generic SQLite|org.sqlite.JDBC|" + + "jdbc:sqlite:test|sa", + "Generic DB2|com.ibm.db2.jcc.DB2Driver|" + + "jdbc:db2://localhost/test|" , + "Generic Oracle|oracle.jdbc.driver.OracleDriver|" + + "jdbc:oracle:thin:@localhost:1521:XE|sa" , + "Generic MS SQL Server 2000|com.microsoft.jdbc.sqlserver.SQLServerDriver|" + + "jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=sqlexpress|sa", + "Generic MS SQL Server 2005|com.microsoft.sqlserver.jdbc.SQLServerDriver|" + + "jdbc:sqlserver://localhost;DatabaseName=test|sa", + "Generic PostgreSQL|org.postgresql.Driver|" + + "jdbc:postgresql:test|" , + "Generic MySQL|com.mysql.cj.jdbc.Driver|" + + "jdbc:mysql://localhost:3306/test|" , + "Generic MariaDB|org.mariadb.jdbc.Driver|" + + "jdbc:mariadb://localhost:3306/test|" , + "Generic HSQLDB|org.hsqldb.jdbcDriver|" + + "jdbc:hsqldb:test;hsqldb.default_table_type=cached|sa" , + "Generic Derby (Server)|org.apache.derby.client.ClientAutoloadedDriver|" + + "jdbc:derby://localhost:1527/test;create=true|sa", + "Generic Derby (Embedded)|org.apache.derby.iapi.jdbc.AutoloadedDriver|" + + "jdbc:derby:test;create=true|sa", + "Generic H2 (Server)|org.h2.Driver|" + + "jdbc:h2:tcp://localhost/~/test|sa", + // this will be listed on top for new installations + "Generic H2 (Embedded)|org.h2.Driver|" + + "jdbc:h2:~/test|sa", + }; + + private static int ticker; + + /** + * The session timeout (the default is 30 minutes). + */ + private static final long SESSION_TIMEOUT = SysProperties.CONSOLE_TIMEOUT; + +// public static void main(String... args) throws IOException { +// String s = IOUtils.readStringAndClose(new java.io.FileReader( +// // "src/main/org/h2/server/web/res/_text_cs.prop"), -1); +// "src/main/org/h2/res/_messages_cs.prop"), -1); +// System.out.println(StringUtils.javaEncode("...")); +// String[] list = Locale.getISOLanguages(); +// for (int i = 0; i < list.length; i++) { +// System.out.print(list[i] + " "); +// } +// System.out.println(); +// String l = "de"; +// String lang = new java.util.Locale(l). +// getDisplayLanguage(new java.util.Locale(l)); +// System.out.println(new java.util.Locale(l).getDisplayLanguage()); +// System.out.println(lang); +// java.util.Locale.CHINESE.getDisplayLanguage(java.util.Locale.CHINESE); +// for (int i = 0; i < lang.length(); i++) { +// System.out.println(Integer.toHexString(lang.charAt(i)) + " "); +// } +// } + + // private URLClassLoader urlClassLoader; + private int port; + private boolean allowOthers; + private boolean isDaemon; + private final Set running = + Collections.synchronizedSet(new HashSet()); + private boolean ssl; + private byte[] adminPassword; + private final HashMap connInfoMap = new HashMap<>(); + + private long lastTimeoutCheck; + private final HashMap sessions = new HashMap<>(); + private final HashSet languages = new HashSet<>(); + private String startDateTime; + private ServerSocket serverSocket; + private String url; + private ShutdownHandler shutdownHandler; + private Thread listenerThread; + private boolean ifExists = true; + private String key; + private boolean allowSecureCreation; + private boolean trace; + private TranslateThread translateThread; + private boolean allowChunked = true; + private String serverPropertiesDir = Constants.SERVER_PROPERTIES_DIR; + // null means the history is not allowed to be stored + private String commandHistoryString; + + /** + * Read the given file from the file system or from the resources. + * + * @param file the file name + * @return the data + */ + byte[] getFile(String file) throws IOException { + trace("getFile <" + file + ">"); + byte[] data = Utils.getResource("/org/h2/server/web/res/" + file); + if (data == null) { + trace(" null"); + } else { + trace(" size=" + data.length); + } + return data; + } + + /** + * Remove this web thread from the set of running threads. + * + * @param t the thread to remove + */ + synchronized void remove(WebThread t) { + running.remove(t); + } + + private static String generateSessionId() { + byte[] buff = MathUtils.secureRandomBytes(16); + return StringUtils.convertBytesToHex(buff); + } + + /** + * Get the web session object for the given session id. + * + * @param sessionId the session id + * @return the web session or null + */ + WebSession getSession(String sessionId) { + long now = System.currentTimeMillis(); + if (lastTimeoutCheck + SESSION_TIMEOUT < now) { + for (String id : new ArrayList<>(sessions.keySet())) { + WebSession session = sessions.get(id); + if (session.lastAccess + SESSION_TIMEOUT < now) { + trace("timeout for " + id); + sessions.remove(id); + } + } + lastTimeoutCheck = now; + } + WebSession session = sessions.get(sessionId); + if (session != null) { + session.lastAccess = System.currentTimeMillis(); + } + return session; + } + + /** + * Create a new web session id and object. + * + * @param hostAddr the host address + * @return the web session object + */ + WebSession createNewSession(String hostAddr) { + String newId; + do { + newId = generateSessionId(); + } while (sessions.get(newId) != null); + WebSession session = new WebSession(this); + session.lastAccess = System.currentTimeMillis(); + session.put("sessionId", newId); + session.put("ip", hostAddr); + session.put("language", DEFAULT_LANGUAGE); + session.put("frame-border", "0"); + session.put("frameset-border", "4"); + sessions.put(newId, session); + // always read the english translation, + // so that untranslated text appears at least in english + readTranslations(session, DEFAULT_LANGUAGE); + return getSession(newId); + } + + String getStartDateTime() { + if (startDateTime == null) { + startDateTime = DateTimeFormatter.ofPattern("EEE, d MMM yyyy HH:mm:ss z", Locale.ENGLISH) + .format(ZonedDateTime.now(ZoneId.of("UTC"))); + } + return startDateTime; + } + + /** + * Returns the key for privileged connections. + * + * @return key key, or null + */ + String getKey() { + return key; + } + + /** + * Sets the key for privileged connections. + * + * @param key key, or null + */ + public void setKey(String key) { + if (!allowOthers) { + this.key = key; + } + } + + /** + * @param allowSecureCreation + * whether creation of databases using the key should be allowed + */ + public void setAllowSecureCreation(boolean allowSecureCreation) { + if (!allowOthers) { + this.allowSecureCreation = allowSecureCreation; + } + } + + @Override + public void init(String... args) { + // set the serverPropertiesDir, because it's used in loadProperties() + for (int i = 0; args != null && i < args.length; i++) { + if ("-properties".equals(args[i])) { + serverPropertiesDir = args[++i]; + } + } + Properties prop = loadProperties(); + port = SortedProperties.getIntProperty(prop, + "webPort", Constants.DEFAULT_HTTP_PORT); + ssl = SortedProperties.getBooleanProperty(prop, + "webSSL", false); + allowOthers = SortedProperties.getBooleanProperty(prop, + "webAllowOthers", false); + setAdminPassword(SortedProperties.getStringProperty(prop, "webAdminPassword", null)); + commandHistoryString = prop.getProperty(COMMAND_HISTORY); + for (int i = 0; args != null && i < args.length; i++) { + String a = args[i]; + if (Tool.isOption(a, "-webPort")) { + port = Integer.decode(args[++i]); + } else if (Tool.isOption(a, "-webSSL")) { + ssl = true; + } else if (Tool.isOption(a, "-webAllowOthers")) { + allowOthers = true; + } else if (Tool.isOption(a, "-webDaemon")) { + isDaemon = true; + } else if (Tool.isOption(a, "-baseDir")) { + String baseDir = args[++i]; + SysProperties.setBaseDir(baseDir); + } else if (Tool.isOption(a, "-ifExists")) { + ifExists = true; + } else if (Tool.isOption(a, "-ifNotExists")) { + ifExists = false; + } else if (Tool.isOption(a, "-webAdminPassword")) { + setAdminPassword(args[++i]); + } else if (Tool.isOption(a, "-properties")) { + // already set + i++; + } else if (Tool.isOption(a, "-trace")) { + trace = true; + } + } +// if (driverList != null) { +// try { +// String[] drivers = +// StringUtils.arraySplit(driverList, ',', false); +// URL[] urls = new URL[drivers.length]; +// for(int i=0; i entry : text.entrySet()) { + String value = (String) entry.getValue(); + if (value.startsWith("#")) { + entry.setValue(value.substring(1)); + } + } + } catch (IOException e) { + DbException.traceThrowable(e); + } + session.put("text", new HashMap<>(text)); + } + + ArrayList> getSessions() { + ArrayList> list = new ArrayList<>(sessions.size()); + for (WebSession s : sessions.values()) { + list.add(s.getInfo()); + } + return list; + } + + @Override + public String getType() { + return "Web Console"; + } + + @Override + public String getName() { + return "H2 Console Server"; + } + + void setAllowOthers(boolean b) { + if (b) { + key = null; + } + allowOthers = b; + } + + @Override + public boolean getAllowOthers() { + return allowOthers; + } + + void setSSL(boolean b) { + ssl = b; + } + + void setPort(int port) { + this.port = port; + } + + boolean getSSL() { + return ssl; + } + + @Override + public int getPort() { + return port; + } + + public boolean isCommandHistoryAllowed() { + return commandHistoryString != null; + } + + public void setCommandHistoryAllowed(boolean allowed) { + if (allowed) { + if (commandHistoryString == null) { + commandHistoryString = ""; + } + } else { + commandHistoryString = null; + } + } + + public ArrayList getCommandHistoryList() { + ArrayList result = new ArrayList<>(); + if (commandHistoryString == null) { + return result; + } + + // Split the commandHistoryString on non-escaped semicolons + // and unescape it. + StringBuilder sb = new StringBuilder(); + for (int end = 0;; end++) { + if (end == commandHistoryString.length() || + commandHistoryString.charAt(end) == ';') { + if (sb.length() > 0) { + result.add(sb.toString()); + sb.delete(0, sb.length()); + } + if (end == commandHistoryString.length()) { + break; + } + } else if (commandHistoryString.charAt(end) == '\\' && + end < commandHistoryString.length() - 1) { + sb.append(commandHistoryString.charAt(++end)); + } else { + sb.append(commandHistoryString.charAt(end)); + } + } + return result; + } + + /** + * Save the command history to the properties file. + * + * @param commandHistory the history + */ + public void saveCommandHistoryList(ArrayList commandHistory) { + StringBuilder sb = new StringBuilder(); + for (String s : commandHistory) { + if (sb.length() > 0) { + sb.append(';'); + } + sb.append(s.replace("\\", "\\\\").replace(";", "\\;")); + } + commandHistoryString = sb.toString(); + saveProperties(null); + } + + /** + * Get the connection information for this setting. + * + * @param name the setting name + * @return the connection information + */ + ConnectionInfo getSetting(String name) { + return connInfoMap.get(name); + } + + /** + * Update a connection information setting. + * + * @param info the connection information + */ + void updateSetting(ConnectionInfo info) { + connInfoMap.put(info.name, info); + info.lastAccess = ticker++; + } + + /** + * Remove a connection information setting from the list + * + * @param name the setting to remove + */ + void removeSetting(String name) { + connInfoMap.remove(name); + } + + private Properties loadProperties() { + try { + if ("null".equals(serverPropertiesDir)) { + return new Properties(); + } + return SortedProperties.loadProperties( + serverPropertiesDir + "/" + Constants.SERVER_PROPERTIES_NAME); + } catch (Exception e) { + DbException.traceThrowable(e); + return new Properties(); + } + } + + /** + * Get the list of connection information setting names. + * + * @return the connection info names + */ + String[] getSettingNames() { + ArrayList list = getSettings(); + String[] names = new String[list.size()]; + for (int i = 0; i < list.size(); i++) { + names[i] = list.get(i).name; + } + return names; + } + + /** + * Get the list of connection info objects. + * + * @return the list + */ + synchronized ArrayList getSettings() { + ArrayList settings = new ArrayList<>(); + if (connInfoMap.size() == 0) { + Properties prop = loadProperties(); + if (prop.size() == 0) { + for (String gen : GENERIC) { + ConnectionInfo info = new ConnectionInfo(gen); + settings.add(info); + updateSetting(info); + } + } else { + for (int i = 0;; i++) { + String data = prop.getProperty(Integer.toString(i)); + if (data == null) { + break; + } + ConnectionInfo info = new ConnectionInfo(data); + settings.add(info); + updateSetting(info); + } + } + } else { + settings.addAll(connInfoMap.values()); + } + Collections.sort(settings); + return settings; + } + + /** + * Save the settings to the properties file. + * + * @param prop null or the properties webPort, webAllowOthers, and webSSL + */ + synchronized void saveProperties(Properties prop) { + try { + if (prop == null) { + Properties old = loadProperties(); + prop = new SortedProperties(); + prop.setProperty("webPort", + Integer.toString(SortedProperties.getIntProperty(old, "webPort", port))); + prop.setProperty("webAllowOthers", + Boolean.toString(SortedProperties.getBooleanProperty(old, "webAllowOthers", allowOthers))); + prop.setProperty("webSSL", + Boolean.toString(SortedProperties.getBooleanProperty(old, "webSSL", ssl))); + if (adminPassword != null) { + prop.setProperty("webAdminPassword", StringUtils.convertBytesToHex(adminPassword)); + } + if (commandHistoryString != null) { + prop.setProperty(COMMAND_HISTORY, commandHistoryString); + } + } + ArrayList settings = getSettings(); + int len = settings.size(); + for (int i = 0; i < len; i++) { + ConnectionInfo info = settings.get(i); + if (info != null) { + prop.setProperty(Integer.toString(len - i - 1), info.getString()); + } + } + if (!"null".equals(serverPropertiesDir)) { + OutputStream out = FileUtils.newOutputStream( + serverPropertiesDir + "/" + Constants.SERVER_PROPERTIES_NAME, false); + prop.store(out, "H2 Server Properties"); + out.close(); + } + } catch (Exception e) { + DbException.traceThrowable(e); + } + } + + /** + * Open a database connection. + * + * @param driver the driver class name + * @param databaseUrl the database URL + * @param user the user name + * @param password the password + * @param userKey the key of privileged user + * @param networkConnectionInfo the network connection information + * @return the database connection + */ + Connection getConnection(String driver, String databaseUrl, String user, + String password, String userKey, NetworkConnectionInfo networkConnectionInfo) throws SQLException { + driver = driver.trim(); + databaseUrl = databaseUrl.trim(); + if (databaseUrl.startsWith("jdbc:h2:")) { + if (!allowSecureCreation || key == null || !key.equals(userKey)) { + if (ifExists) { + databaseUrl += ";FORBID_CREATION=false"; + } + } + } + // do not trim the password, otherwise an + // encrypted H2 database with empty user password doesn't work + return JdbcUtils.getConnection(driver, databaseUrl, user.trim(), password, networkConnectionInfo); + } + + /** + * Shut down the web server. + */ + void shutdown() { + if (shutdownHandler != null) { + shutdownHandler.shutdown(); + } + } + + public void setShutdownHandler(ShutdownHandler shutdownHandler) { + this.shutdownHandler = shutdownHandler; + } + + /** + * Create a session with a given connection. + * + * @param conn the connection + * @return the URL of the web site to access this connection + */ + public String addSession(Connection conn) throws SQLException { + WebSession session = createNewSession("local"); + session.setShutdownServerOnDisconnect(); + session.setConnection(conn); + session.put("url", conn.getMetaData().getURL()); + String s = (String) session.get("sessionId"); + return url + "/frame.jsp?jsessionid=" + s; + } + + /** + * The translate thread reads and writes the file translation.properties + * once a second. + */ + private class TranslateThread extends Thread { + + private final Path file = Paths.get("translation.properties"); + private final Map translation; + private volatile boolean stopNow; + + TranslateThread(Map translation) { + this.translation = translation; + } + + public String getFileName() { + return file.toAbsolutePath().toString(); + } + + public void stopNow() { + this.stopNow = true; + try { + join(); + } catch (InterruptedException e) { + // ignore + } + } + + @Override + public void run() { + while (!stopNow) { + try { + SortedProperties sp = new SortedProperties(); + if (Files.exists(file)) { + InputStream in = Files.newInputStream(file); + sp.load(in); + translation.putAll(sp); + } else { + OutputStream out = Files.newOutputStream(file); + sp.putAll(translation); + sp.store(out, "Translation"); + } + Thread.sleep(1000); + } catch (Exception e) { + traceError(e); + } + } + } + + } + + /** + * Start the translation thread that reads the file once a second. + * + * @param translation the translation map + * @return the name of the file to translate + */ + String startTranslate(Map translation) { + if (translateThread != null) { + translateThread.stopNow(); + } + translateThread = new TranslateThread(translation); + translateThread.setDaemon(true); + translateThread.start(); + return translateThread.getFileName(); + } + + @Override + public boolean isDaemon() { + return isDaemon; + } + + void setAllowChunked(boolean allowChunked) { + this.allowChunked = allowChunked; + } + + boolean getAllowChunked() { + return allowChunked; + } + + byte[] getAdminPassword() { + return adminPassword; + } + + void setAdminPassword(String password) { + if (password == null || password.isEmpty()) { + adminPassword = null; + return; + } + if (password.length() == 128) { + try { + adminPassword = StringUtils.convertHexToBytes(password); + return; + } catch (Exception ex) {} + } + byte[] salt = MathUtils.secureRandomBytes(32); + byte[] hash = SHA256.getHashWithSalt(password.getBytes(StandardCharsets.UTF_8), salt); + byte[] total = Arrays.copyOf(salt, 64); + System.arraycopy(hash, 0, total, 32, 32); + adminPassword = total; + } + + /** + * Check the admin password. + * + * @param password the password to test + * @return true if admin password not configure, or admin password correct + */ + boolean checkAdminPassword(String password) { + if (adminPassword == null) { + return false; + } + byte[] salt = Arrays.copyOf(adminPassword, 32); + byte[] hash = new byte[32]; + System.arraycopy(adminPassword, 32, hash, 0, 32); + return Utils.compareSecure(hash, SHA256.getHashWithSalt(password.getBytes(StandardCharsets.UTF_8), salt)); + } + +} diff --git a/h2/src/main/org/h2/tools/Server.java b/h2/src/main/org/h2/tools/Server.java index 10b7111845..a2eee26cb0 100644 --- a/h2/src/main/org/h2/tools/Server.java +++ b/h2/src/main/org/h2/tools/Server.java @@ -122,6 +122,7 @@ public static void main(String... args) throws SQLException { new Server().runTool(args); } + // 验证参数 private void verifyArgs(String... args) throws SQLException { for (int i = 0; args != null && i < args.length; i++) { String arg = args[i]; @@ -301,7 +302,7 @@ public void runTool(String... args) throws SQLException { } } verifyArgs(args); - if (startDefaultServers) { + if (startDefaultServers) { // 如果没有设置启动服务类型,则所有的类型都会启动 tcpStart = true; pgStart = true; webStart = true; @@ -315,10 +316,11 @@ public void runTool(String... args) throws SQLException { } try { if (tcpStart) { - tcp = createTcpServer(args); - tcp.start(); - out.println(tcp.getStatus()); - tcp.setShutdownHandler(this); + tcp = createTcpServer(args); // 创建服务 + tcp.start(); // 启动服务 + out.println(tcp.getStatus()); //打印服务状态 + tcp.setShutdownHandler(this); // 设置关闭处理函数?在创建Tcp已经塞了一次,为什么外边又要塞一次?有单独调用CreateTcpServer方法的地方,怕遗漏? + // 或者赛入的不是同一个Server? } if (pgStart) { pg = createPgServer(args); @@ -470,9 +472,9 @@ static Server createWebServer(String[] args, String key, boolean allowSecureCrea * @return the server */ public static Server createTcpServer(String... args) throws SQLException { - TcpServer service = new TcpServer(); - Server server = new Server(service, args); - service.setShutdownHandler(server); + TcpServer tcpServer = new TcpServer(); // 创建一个服务 + Server server = new Server(tcpServer, args); // 作为当前执行线程参数传入进入 + tcpServer.setShutdownHandler(server); // 服务的关闭处理服务,为当前执行线程 return server; } @@ -505,10 +507,10 @@ public static Server createPgServer(String... args) throws SQLException { * @return the server if successful * @throws SQLException if the server could not be started */ - public Server start() throws SQLException { + public Server start() throws SQLException { // 服务启动入口 try { started = true; - service.start(); + service.start(); // 各个实现Service的Start方法,比如TcpServer.start()、WebServer.start() String url = service.getURL(); int idx = url.indexOf('?'); if (idx >= 0) { diff --git a/learning-docs/0518 b/learning-docs/0518 new file mode 100644 index 0000000000..d46f37c010 --- /dev/null +++ b/learning-docs/0518 @@ -0,0 +1,7 @@ +1.服务跑起来需要配置maven安装路径及对应的settings文件 +2.测试文件参考MyH2Server.java, 添加baseDir参数,设置路径参数及值 +list.add("-web"); +list.add("-baseDir"); +list.add("E:/devCode/H2-Research"); + +3.页面console中要添加以baseDir为父路径的数据库地址,即JDBC URL:jdbc:h2:E:/devCode/H2-Research/test \ No newline at end of file diff --git a/my-test/my/test/MyH2Server.java b/my-test/my/test/MyH2Server.java index c7d5d278a6..70c225bb46 100644 --- a/my-test/my/test/MyH2Server.java +++ b/my-test/my/test/MyH2Server.java @@ -1,50 +1,52 @@ -package my.test; - -import java.sql.SQLException; -import java.util.ArrayList; - -public class MyH2Server { - public static void main(String[] args) throws SQLException { - // System.setProperty("DATABASE_TO_UPPER", "false"); - System.setProperty("h2.lobInDatabase", "false"); - System.setProperty("h2.lobClientMaxSizeMemory", "1024"); - System.setProperty("java.io.tmpdir", "./target/mytest/tmp"); - System.setProperty("h2.baseDir", "./target/mytest"); - // System.setProperty("h2.check2", "true"); - ArrayList list = new ArrayList(); - // list.add("-tcp"); - // //list.add("-tool"); - // org.h2.tools.Server.main(list.toArray(new String[list.size()])); - - //list.add("-help"); - //list.add("-webXXXX"); //测试showUsageAndThrowUnsupportedOption - - list.add("-tcp"); - list.add("-tcpPort"); - list.add("9092"); - - list.add("-pg"); - list.add("-pgPort"); - list.add("9511"); - - // 测试org.h2.server.TcpServer.checkKeyAndGetDatabaseName(String) - // list.add("-key"); - // list.add("mydb"); - // list.add("mydatabase"); - - - // list.add("-browser"); - - // list.add("-pg"); - // list.add("-tcp"); - // list.add("-web"); - // list.add("-ifExists"); - list.add("-ifNotExists"); - list.add("-tcpAllowOthers"); - list.add("-tcpPassword"); - list.add("aaa"); - list.add("-webAdminPassword"); - list.add("aaa"); - org.h2.tools.Server.main(list.toArray(new String[list.size()])); - } -} +package my.test; + +import java.sql.SQLException; +import java.util.ArrayList; + +public class MyH2Server { + public static void main(String[] args) throws SQLException { + // System.setProperty("DATABASE_TO_UPPER", "false"); + System.setProperty("h2.lobInDatabase", "false"); + System.setProperty("h2.lobClientMaxSizeMemory", "1024"); + System.setProperty("java.io.tmpdir", "./target/mytest/tmp"); + System.setProperty("h2.baseDir", "./target/mytest"); + // System.setProperty("h2.check2", "true"); + ArrayList list = new ArrayList(); + // list.add("-tcp"); + // //list.add("-tool"); + // org.h2.tools.Server.main(list.toArray(new String[list.size()])); + + //list.add("-help"); + //list.add("-webXXXX"); //测试showUsageAndThrowUnsupportedOption + + list.add("-tcp"); +// list.add("-tcpPort"); +// list.add("9092"); +// +// list.add("-pg"); +// list.add("-pgPort"); +// list.add("9511"); + + // 测试org.h2.server.TcpServer.checkKeyAndGetDatabaseName(String) + // list.add("-key"); + // list.add("mydb"); + // list.add("mydatabase"); + +// +// list.add("-browser"); + + // list.add("-pg"); + // list.add("-tcp"); + list.add("-web"); + list.add("-baseDir"); + list.add("E:/devCode/H2-Research"); + // list.add("-ifExists"); +// list.add("-ifNotExists"); +// list.add("-tcpAllowOthers"); +// list.add("-tcpPassword"); +// list.add("aaa"); +// list.add("-webAdminPassword"); +// list.add("aaa"); + org.h2.tools.Server.main(list.toArray(new String[list.size()])); + } +} diff --git a/pom.xml b/pom.xml index 66a8c49d80..11922d16eb 100644 --- a/pom.xml +++ b/pom.xml @@ -1,201 +1,201 @@ - - 4.0.0 - - com.h2database - h2 - 1.4.201-SNAPSHOT - jar - h2-research - https://h2database.com - H2 Database Engine - - - - MPL 2.0 - https://www.mozilla.org/en-US/MPL/2.0/ - repo - - - EPL 1.0 - https://opensource.org/licenses/eclipse-1.0.php - repo - - - - - scm:git:https://github.com/h2database/h2database - https://github.com/h2database/h2database - - - - - thomas.tom.mueller - Thomas Mueller - thomas.tom.mueller at gmail dot com - - - - - 1.8 - 1.8 - 7.2 - 1.16.1 - 5.5.2 - 8.2.0 - 5.0.0 - 42.2.8 - 4.0.1 - 1.7.28 - UTF-8 - - - - - - junit - junit - 4.8 - test - - - - javax.servlet - javax.servlet-api - ${servlet.version} - - - org.apache.lucene - lucene-core - ${lucene.version} - - - org.apache.lucene - lucene-analyzers-common - ${lucene.version} - - - org.apache.lucene - lucene-queryparser - ${lucene.version} - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.osgi - org.osgi.core - ${osgi.version} - - - org.osgi - org.osgi.enterprise - ${osgi.version} - - - org.locationtech.jts - jts-core - ${jts.version} - - - - - - - org.slf4j - slf4j-nop - ${slf4j.version} - test - - - org.postgresql - postgresql - ${pgjdbc.version} - test - - - org.junit.jupiter - junit-jupiter-engine - ${junit.version} - test - - - org.ow2.asm - asm - ${asm.version} - test - - - - - - - - jigsaw-jdk - - [1.9,) - - - - - default-tools.jar - - - ${java.home}/../lib/tools.jar - - - - - com.sun - tools - system - 1.8 - ${java.home}/../lib/tools.jar - - - - - default-tools.jar-mac - - - ${java.home}/../Classes/classes.jar - - - - - com.sun - tools - system - 1.8 - ${java.home}/../Classes/classes.jar - - - - - - - h2/src/main - my-test - - - h2/src/main - - **/*.prop - **/*.png - **/*.jsp - **/*.ico - **/*.gif - **/*.css - **/*.js - org/h2/res/help.csv - org/h2/res/javadoc.properties - org/h2/server/pg/pg_catalog.sql - META-INF/** - - - - - + + 4.0.0 + + com.h2database + h2 + 1.4.201-SNAPSHOT + jar + h2-research + https://h2database.com + H2 Database Engine + + + + MPL 2.0 + https://www.mozilla.org/en-US/MPL/2.0/ + repo + + + EPL 1.0 + https://opensource.org/licenses/eclipse-1.0.php + repo + + + + + scm:git:https://github.com/h2database/h2database + https://github.com/h2database/h2database + + + + + thomas.tom.mueller + Thomas Mueller + thomas.tom.mueller at gmail dot com + + + + + 1.8 + 1.8 + 7.2 + 1.16.1 + 5.5.2 + 8.2.0 + 5.0.0 + 42.2.8 + 4.0.1 + 1.7.28 + UTF-8 + + + + + + junit + junit + 4.8 + test + + + + javax.servlet + javax.servlet-api + ${servlet.version} + + + org.apache.lucene + lucene-core + ${lucene.version} + + + org.apache.lucene + lucene-analyzers-common + ${lucene.version} + + + org.apache.lucene + lucene-queryparser + ${lucene.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.osgi + org.osgi.core + ${osgi.version} + + + org.osgi + org.osgi.enterprise + ${osgi.version} + + + org.locationtech.jts + jts-core + ${jts.version} + + + + + + + org.slf4j + slf4j-nop + ${slf4j.version} + test + + + org.postgresql + postgresql + ${pgjdbc.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.ow2.asm + asm + ${asm.version} + test + + + + + + + + jigsaw-jdk + + [1.9,) + + + + + default-tools.jar + + + ${java.home}/../lib/tools.jar + + + + + com.sun + tools + system + 1.18 + ${java.home}/../lib/tools.jar + + + + + default-tools.jar-mac + + + ${java.home}/../Classes/classes.jar + + + + + com.sun + tools + system + 1.8 + ${java.home}/../Classes/classes.jar + + + + + + + h2/src/main + my-test + + + h2/src/main + + **/*.prop + **/*.png + **/*.jsp + **/*.ico + **/*.gif + **/*.css + **/*.js + org/h2/res/help.csv + org/h2/res/javadoc.properties + org/h2/server/pg/pg_catalog.sql + META-INF/** + + + + +