Unverified 提交 e329948b authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1726 from katzyn/ifNotExists

Disable remote database creation by default
...@@ -1200,7 +1200,7 @@ options <code>-webAllowOthers, -tcpAllowOthers, -pgAllowOthers</code>. ...@@ -1200,7 +1200,7 @@ options <code>-webAllowOthers, -tcpAllowOthers, -pgAllowOthers</code>.
<p> <p>
If you enable remote access using If you enable remote access using
<code>-tcpAllowOthers</code> or <code>-pgAllowOthers</code>, <code>-tcpAllowOthers</code> or <code>-pgAllowOthers</code>,
please also consider using the options <code>-baseDir, -ifExists</code>, please also consider using the options <code>-baseDir</code>,
so that remote users can not create new databases so that remote users can not create new databases
or access existing databases with weak passwords. or access existing databases with weak passwords.
When using the option <code>-baseDir</code>, only databases within that directory may be accessed. When using the option <code>-baseDir</code>, only databases within that directory may be accessed.
...@@ -1209,7 +1209,7 @@ Ensure the existing accessible databases are protected using strong passwords. ...@@ -1209,7 +1209,7 @@ Ensure the existing accessible databases are protected using strong passwords.
<p> <p>
If you enable remote access using <code>-webAllowOthers</code>, If you enable remote access using <code>-webAllowOthers</code>,
please ensure the web server can only be accessed from trusted networks. please ensure the web server can only be accessed from trusted networks.
The options <code>-baseDir, -ifExists</code> don't protect The options <code>-baseDir</code> don't protect
access to the tools section, prevent remote shutdown of the web server, access to the tools section, prevent remote shutdown of the web server,
changes to the preferences, the saved connection settings, changes to the preferences, the saved connection settings,
or access to other databases accessible from the system. or access to other databases accessible from the system.
...@@ -1422,25 +1422,25 @@ CREATE SPATIAL INDEX GEO_TABLE_SPATIAL_INDEX ON GEO_TABLE(THE_GEOM); ...@@ -1422,25 +1422,25 @@ CREATE SPATIAL INDEX GEO_TABLE_SPATIAL_INDEX ON GEO_TABLE(THE_GEOM);
</pre> </pre>
<p> <p>
To query the table using geometry envelope intersection, To query the table using geometry envelope intersection,
use the operation <code>&&</code>, as in PostGIS: use the operation <code>&amp;&amp;</code>, as in PostGIS:
</p> </p>
<pre> <pre>
SELECT * FROM GEO_TABLE SELECT * FROM GEO_TABLE
WHERE THE_GEOM && 'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))'; WHERE THE_GEOM &amp;&amp; 'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))';
</pre> </pre>
<p> <p>
You can verify that the spatial index is used using the "explain plan" feature: You can verify that the spatial index is used using the "explain plan" feature:
</p> </p>
<pre> <pre>
EXPLAIN SELECT * FROM GEO_TABLE EXPLAIN SELECT * FROM GEO_TABLE
WHERE THE_GEOM && 'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))'; WHERE THE_GEOM &amp;&amp; 'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))';
-- Result -- Result
SELECT SELECT
GEO_TABLE.GID, GEO_TABLE.GID,
GEO_TABLE.THE_GEOM GEO_TABLE.THE_GEOM
FROM PUBLIC.GEO_TABLE FROM PUBLIC.GEO_TABLE
/* PUBLIC.GEO_TABLE_SPATIAL_INDEX: /* PUBLIC.GEO_TABLE_SPATIAL_INDEX:
THE_GEOM && 'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))' */ THE_GEOM &amp;&amp; 'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))' */
WHERE INTERSECTS(THE_GEOM, WHERE INTERSECTS(THE_GEOM,
'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))') 'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))')
</pre> </pre>
......
...@@ -97,7 +97,8 @@ No, currently commercial support is not available. ...@@ -97,7 +97,8 @@ No, currently commercial support is not available.
<h3 id="create_database">How to Create a New Database?</h3> <h3 id="create_database">How to Create a New Database?</h3>
<p> <p>
By default, a new database is automatically created if it does not yet exist. By default, a new database is automatically created if it does not yet exist when
<a href="features.html#database_url">embedded</a> URL is used.
See <a href="tutorial.html#creating_new_databases">Creating New Databases</a>. See <a href="tutorial.html#creating_new_databases">Creating New Databases</a>.
</p> </p>
......
...@@ -499,7 +499,8 @@ For more information about the algorithms, see ...@@ -499,7 +499,8 @@ For more information about the algorithms, see
<h2 id="database_only_if_exists">Opening a Database Only if it Already Exists</h2> <h2 id="database_only_if_exists">Opening a Database Only if it Already Exists</h2>
<p> <p>
By default, when an application calls <code>DriverManager.getConnection(url, ...)</code> By default, when an application calls <code>DriverManager.getConnection(url, ...)</code>
and the database specified in the URL does not yet exist, a new (empty) database is created. with <a href="features.html#database_url">embedded</a> URL and the database specified in the URL does not yet exist,
a new (empty) database is created.
In some situations, it is better to restrict creating new databases, and only allow to open In some situations, it is better to restrict creating new databases, and only allow to open
existing databases. To do this, add <code>;IFEXISTS=TRUE</code> existing databases. To do this, add <code>;IFEXISTS=TRUE</code>
to the database URL. In this case, if the database does not already exist, an exception is thrown when to the database URL. In this case, if the database does not already exist, an exception is thrown when
......
...@@ -436,6 +436,7 @@ Supported settings are: ...@@ -436,6 +436,7 @@ Supported settings are:
<ul><li><code>webAllowOthers</code>: allow other computers to connect. <ul><li><code>webAllowOthers</code>: allow other computers to connect.
</li><li><code>webPort</code>: the port of the H2 Console </li><li><code>webPort</code>: the port of the H2 Console
</li><li><code>webSSL</code>: use encrypted TLS (HTTPS) connections. </li><li><code>webSSL</code>: use encrypted TLS (HTTPS) connections.
</li><li><code>webAdminPassword</code>: password to access preferences and tools of H2 Console.
</li></ul> </li></ul>
<p> <p>
In addition to those settings, the properties of the last recently used connection In addition to those settings, the properties of the last recently used connection
...@@ -474,14 +475,18 @@ In this database, user names are not case sensitive, but passwords are. ...@@ -474,14 +475,18 @@ In this database, user names are not case sensitive, but passwords are.
<h2 id="creating_new_databases">Creating New Databases</h2> <h2 id="creating_new_databases">Creating New Databases</h2>
<p> <p>
By default, if the database specified in the URL does not yet exist, a new (empty) By default, if the database specified in the <a href="features.html#database_url">embedded</a> URL does not yet exist,
database is created automatically. The user that created the database automatically becomes a new (empty) database is created automatically.
the administrator of this database. The user that created the database automatically becomes the administrator of this database.
</p> </p>
<p> <p>
Auto-creating new database can be disabled, see Auto-creation of databases can be disabled, see
<a href="features.html#database_only_if_exists">Opening a Database Only if it Already Exists</a>. <a href="features.html#database_only_if_exists">Opening a Database Only if it Already Exists</a>.
</p> </p>
<p>
H2 Console does not allow creation of databases by default.
If H2 Console is launched with icon in the system tray its context menu can be used to create a new database.
</p>
<h2 id="using_server">Using the Server</h2> <h2 id="using_server">Using the Server</h2>
<p> <p>
......
...@@ -611,13 +611,13 @@ public class ErrorCode { ...@@ -611,13 +611,13 @@ public class ErrorCode {
public static final int PARAMETER_NOT_SET_1 = 90012; public static final int PARAMETER_NOT_SET_1 = 90012;
/** /**
* The error with code <code>90013</code> is thrown when * The error with code <code>90013</code> is thrown when trying to open a
* trying to open a database that does not exist using the flag * database that does not exist remotely without enabling remote database
* IFEXISTS=TRUE, or when trying to access a database object with a catalog * creation first, or using the flag IFEXISTS=TRUE, or when trying to access
* name that does not match the database name. Example: * a database object with a catalog name that does not match the database
* name.
* <pre> * <pre>
* CREATE TABLE TEST(ID INT); * jdbc:h2:./database_that_does_not_exist
* SELECT XYZ.PUBLIC.TEST.ID FROM TEST;
* </pre> * </pre>
*/ */
public static final int DATABASE_NOT_FOUND_1 = 90013; public static final int DATABASE_NOT_FOUND_1 = 90013;
......
...@@ -36,6 +36,6 @@ org.h2.tools.RunScript.main=Options are case sensitive. Supported options are\:\ ...@@ -36,6 +36,6 @@ org.h2.tools.RunScript.main=Options are case sensitive. Supported options are\:\
org.h2.tools.Script=Creates a SQL script file by extracting the schema and data of a database. org.h2.tools.Script=Creates a SQL script file by extracting the schema and data of a database.
org.h2.tools.Script.main=Options are case sensitive. Supported options are\:\n[-help] or [-?] Print the list of options\n[-url "<url>"] The database URL (jdbc\:...)\n[-user <user>] The user name (default\: sa)\n[-password <pwd>] The password\n[-script <file>] The target script file name (default\: backup.sql)\n[-options ...] A list of options (only for embedded H2, see SCRIPT)\n[-quiet] Do not print progress information org.h2.tools.Script.main=Options are case sensitive. Supported options are\:\n[-help] or [-?] Print the list of options\n[-url "<url>"] The database URL (jdbc\:...)\n[-user <user>] The user name (default\: sa)\n[-password <pwd>] The password\n[-script <file>] The target script file name (default\: backup.sql)\n[-options ...] A list of options (only for embedded H2, see SCRIPT)\n[-quiet] Do not print progress information
org.h2.tools.Server=Starts the H2 Console (web-) server, TCP, and PG server. org.h2.tools.Server=Starts the H2 Console (web-) server, TCP, and PG server.
org.h2.tools.Server.main=When running without options, -tcp, -web, -browser and -pg are started.\nOptions are case sensitive. Supported options are\:\n[-help] or [-?] Print the list of options\n[-web] Start the web server with the H2 Console\n[-webAllowOthers] Allow other computers to connect - see below\n[-webDaemon] Use a daemon thread\n[-webPort <port>] The port (default\: 8082)\n[-webSSL] Use encrypted (HTTPS) connections\n[-browser] Start a browser connecting to the web server\n[-tcp] Start the TCP server\n[-tcpAllowOthers] Allow other computers to connect - see below\n[-tcpDaemon] Use a daemon thread\n[-tcpPort <port>] The port (default\: 9092)\n[-tcpSSL] Use encrypted (SSL) connections\n[-tcpPassword <pwd>] The password for shutting down a TCP server\n[-tcpShutdown "<url>"] Stop the TCP server; example\: tcp\://localhost\n[-tcpShutdownForce] Do not wait until all connections are closed\n[-pg] Start the PG server\n[-pgAllowOthers] Allow other computers to connect - see below\n[-pgDaemon] Use a daemon thread\n[-pgPort <port>] The port (default\: 5435)\n[-properties "<dir>"] Server properties (default\: ~, disable\: null)\n[-baseDir <dir>] The base directory for H2 databases (all servers)\n[-ifExists] Only existing databases may be opened (all servers)\n[-trace] Print additional trace information (all servers)\n[-key <from> <to>] Allows to map a database name to another (all servers)\nThe options -xAllowOthers are potentially risky.\nFor details, see Advanced Topics / Protection against Remote Access. org.h2.tools.Server.main=When running without options, -tcp, -web, -browser and -pg are started.\nOptions are case sensitive. Supported options are\:\n[-help] or [-?] Print the list of options\n[-web] Start the web server with the H2 Console\n[-webAllowOthers] Allow other computers to connect - see below\n[-webDaemon] Use a daemon thread\n[-webPort <port>] The port (default\: 8082)\n[-webSSL] Use encrypted (HTTPS) connections\n[-webAdminPassword] Password of DB Console administrator\n[-browser] Start a browser connecting to the web server\n[-tcp] Start the TCP server\n[-tcpAllowOthers] Allow other computers to connect - see below\n[-tcpDaemon] Use a daemon thread\n[-tcpPort <port>] The port (default\: 9092)\n[-tcpSSL] Use encrypted (SSL) connections\n[-tcpPassword <pwd>] The password for shutting down a TCP server\n[-tcpShutdown "<url>"] Stop the TCP server; example\: tcp\://localhost\n[-tcpShutdownForce] Do not wait until all connections are closed\n[-pg] Start the PG server\n[-pgAllowOthers] Allow other computers to connect - see below\n[-pgDaemon] Use a daemon thread\n[-pgPort <port>] The port (default\: 5435)\n[-properties "<dir>"] Server properties (default\: ~, disable\: null)\n[-baseDir <dir>] The base directory for H2 databases (all servers)\n[-ifExists] Only existing databases may be opened (all servers)\n[-ifNotExists] Databases are created when accessed\n[-trace] Print additional trace information (all servers)\n[-key <from> <to>] Allows to map a database name to another (all servers)\nThe options -xAllowOthers are potentially risky.\nFor details, see Advanced Topics / Protection against Remote Access.
org.h2.tools.Shell=Interactive command line tool to access a database using JDBC. org.h2.tools.Shell=Interactive command line tool to access a database using JDBC.
org.h2.tools.Shell.main=Options are case sensitive. Supported options are\:\n[-help] or [-?] Print the list of options\n[-url "<url>"] The database URL (jdbc\:h2\:...)\n[-user <user>] The user name\n[-password <pwd>] The password\n[-driver <class>] The JDBC driver class to use (not required in most cases)\n[-sql "<statements>"] Execute the SQL statements and exit\n[-properties "<dir>"] Load the server properties from this directory\nIf special characters don't work as expected, you may need to use\n -Dfile.encoding\=UTF-8 (Mac OS X) or CP850 (Windows). org.h2.tools.Shell.main=Options are case sensitive. Supported options are\:\n[-help] or [-?] Print the list of options\n[-url "<url>"] The database URL (jdbc\:h2\:...)\n[-user <user>] The user name\n[-password <pwd>] The password\n[-driver <class>] The JDBC driver class to use (not required in most cases)\n[-sql "<statements>"] Execute the SQL statements and exit\n[-properties "<dir>"] Load the server properties from this directory\nIf special characters don't work as expected, you may need to use\n -Dfile.encoding\=UTF-8 (Mac OS X) or CP850 (Windows).
...@@ -63,7 +63,7 @@ public class TcpServer implements Service { ...@@ -63,7 +63,7 @@ public class TcpServer implements Service {
private String baseDir; private String baseDir;
private boolean allowOthers; private boolean allowOthers;
private boolean isDaemon; private boolean isDaemon;
private boolean ifExists; private boolean ifExists = true;
private Connection managementDb; private Connection managementDb;
private PreparedStatement managementDbAdd; private PreparedStatement managementDbAdd;
private PreparedStatement managementDbRemove; private PreparedStatement managementDbRemove;
...@@ -187,6 +187,8 @@ public class TcpServer implements Service { ...@@ -187,6 +187,8 @@ public class TcpServer implements Service {
isDaemon = true; isDaemon = true;
} else if (Tool.isOption(a, "-ifExists")) { } else if (Tool.isOption(a, "-ifExists")) {
ifExists = true; ifExists = true;
} else if (Tool.isOption(a, "-ifNotExists")) {
ifExists = false;
} }
} }
org.h2.Driver.load(); org.h2.Driver.load();
......
...@@ -79,7 +79,7 @@ public class PgServer implements Service { ...@@ -79,7 +79,7 @@ public class PgServer implements Service {
private String baseDir; private String baseDir;
private boolean allowOthers; private boolean allowOthers;
private boolean isDaemon; private boolean isDaemon;
private boolean ifExists; private boolean ifExists = true;
private String key, keyDatabase; private String key, keyDatabase;
@Override @Override
...@@ -100,6 +100,8 @@ public class PgServer implements Service { ...@@ -100,6 +100,8 @@ public class PgServer implements Service {
isDaemon = true; isDaemon = true;
} else if (Tool.isOption(a, "-ifExists")) { } else if (Tool.isOption(a, "-ifExists")) {
ifExists = true; ifExists = true;
} else if (Tool.isOption(a, "-ifNotExists")) {
ifExists = false;
} else if (Tool.isOption(a, "-key")) { } else if (Tool.isOption(a, "-key")) {
key = args[++i]; key = args[++i];
keyDatabase = args[++i]; keyDatabase = args[++i];
......
...@@ -168,6 +168,14 @@ public class WebApp { ...@@ -168,6 +168,14 @@ public class WebApp {
trace(file); trace(file);
if (file.endsWith(".do")) { if (file.endsWith(".do")) {
file = process(file); file = process(file);
} else if (file.endsWith(".jsp")) {
switch (file) {
case "admin.jsp":
case "tools.jsp":
if (!checkAdmin(file)) {
file = process("adminLogin.do");
}
}
} }
return file; return file;
} }
...@@ -207,46 +215,86 @@ public class WebApp { ...@@ -207,46 +215,86 @@ public class WebApp {
private String process(String file) { private String process(String file) {
trace("process " + file); trace("process " + file);
while (file.endsWith(".do")) { while (file.endsWith(".do")) {
if ("login.do".equals(file)) { switch (file) {
case "login.do":
file = login(); file = login();
} else if ("index.do".equals(file)) { break;
case "index.do":
file = index(); file = index();
} else if ("logout.do".equals(file)) { break;
case "logout.do":
file = logout(); file = logout();
} else if ("settingRemove.do".equals(file)) { break;
case "settingRemove.do":
file = settingRemove(); file = settingRemove();
} else if ("settingSave.do".equals(file)) { break;
case "settingSave.do":
file = settingSave(); file = settingSave();
} else if ("test.do".equals(file)) { break;
case "test.do":
file = test(); file = test();
} else if ("query.do".equals(file)) { break;
case "query.do":
file = query(); file = query();
} else if ("tables.do".equals(file)) { break;
case "tables.do":
file = tables(); file = tables();
} else if ("editResult.do".equals(file)) { break;
case "editResult.do":
file = editResult(); file = editResult();
} else if ("getHistory.do".equals(file)) { break;
case "getHistory.do":
file = getHistory(); file = getHistory();
} else if ("admin.do".equals(file)) { break;
file = admin(); case "admin.do":
} else if ("adminSave.do".equals(file)) { file = checkAdmin(file) ? admin() : "adminLogin.do";
file = adminSave(); break;
} else if ("adminStartTranslate.do".equals(file)) { case "adminSave.do":
file = adminStartTranslate(); file = checkAdmin(file) ? adminSave() : "adminLogin.do";
} else if ("adminShutdown.do".equals(file)) { break;
file = adminShutdown(); case "adminStartTranslate.do":
} else if ("autoCompleteList.do".equals(file)) { file = checkAdmin(file) ? adminStartTranslate() : "adminLogin.do";
break;
case "adminShutdown.do":
file = checkAdmin(file) ? adminShutdown() : "adminLogin.do";
break;
case "autoCompleteList.do":
file = autoCompleteList(); file = autoCompleteList();
} else if ("tools.do".equals(file)) { break;
file = tools(); case "tools.do":
} else { file = checkAdmin(file) ? tools() : "adminLogin.do";
break;
case "adminLogin.do":
file = adminLogin();
break;
default:
file = "error.jsp"; file = "error.jsp";
break;
} }
} }
trace("return " + file); trace("return " + file);
return file; return file;
} }
private boolean checkAdmin(String file) {
Boolean b = (Boolean) session.get("admin");
if (b != null && b) {
return true;
}
session.put("adminBack", file);
return false;
}
private String adminLogin() {
String password = attributes.getProperty("password");
if (password == null || password.isEmpty() || !server.checkAdminPassword(password)) {
return "adminLogin.jsp";
}
String back = (String) session.remove("adminBack");
session.put("admin", true);
return back != null ? back : "admin.do";
}
private String autoCompleteList() { private String autoCompleteList() {
String query = (String) attributes.get("query"); String query = (String) attributes.get("query");
boolean lowercase = false; boolean lowercase = false;
...@@ -358,6 +406,10 @@ public class WebApp { ...@@ -358,6 +406,10 @@ public class WebApp {
boolean ssl = Utils.parseBoolean((String) attributes.get("ssl"), false, false); boolean ssl = Utils.parseBoolean((String) attributes.get("ssl"), false, false);
prop.setProperty("webSSL", String.valueOf(ssl)); prop.setProperty("webSSL", String.valueOf(ssl));
server.setSSL(ssl); server.setSSL(ssl);
byte[] adminPassword = server.getAdminPassword();
if (adminPassword != null) {
prop.setProperty("webAdminPassword", StringUtils.convertBytesToHex(adminPassword));
}
server.saveProperties(prop); server.saveProperties(prop);
} catch (Exception e) { } catch (Exception e) {
trace(e.toString()); trace(e.toString());
...@@ -983,6 +1035,7 @@ public class WebApp { ...@@ -983,6 +1035,7 @@ public class WebApp {
} catch (Exception e) { } catch (Exception e) {
trace(e.toString()); trace(e.toString());
} }
session.remove("admin");
return "index.do"; return "index.do";
} }
......
...@@ -16,6 +16,7 @@ import java.sql.Connection; ...@@ -16,6 +16,7 @@ import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
...@@ -28,6 +29,7 @@ import java.util.Set; ...@@ -28,6 +29,7 @@ import java.util.Set;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.security.SHA256;
import org.h2.server.Service; import org.h2.server.Service;
import org.h2.server.ShutdownHandler; import org.h2.server.ShutdownHandler;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
...@@ -154,6 +156,7 @@ public class WebServer implements Service { ...@@ -154,6 +156,7 @@ public class WebServer implements Service {
private final Set<WebThread> running = private final Set<WebThread> running =
Collections.synchronizedSet(new HashSet<WebThread>()); Collections.synchronizedSet(new HashSet<WebThread>());
private boolean ssl; private boolean ssl;
private byte[] adminPassword;
private final HashMap<String, ConnectionInfo> connInfoMap = new HashMap<>(); private final HashMap<String, ConnectionInfo> connInfoMap = new HashMap<>();
private long lastTimeoutCheck; private long lastTimeoutCheck;
...@@ -164,7 +167,7 @@ public class WebServer implements Service { ...@@ -164,7 +167,7 @@ public class WebServer implements Service {
private String url; private String url;
private ShutdownHandler shutdownHandler; private ShutdownHandler shutdownHandler;
private Thread listenerThread; private Thread listenerThread;
private boolean ifExists; private boolean ifExists = true;
private boolean trace; private boolean trace;
private TranslateThread translateThread; private TranslateThread translateThread;
private boolean allowChunked = true; private boolean allowChunked = true;
...@@ -278,6 +281,7 @@ public class WebServer implements Service { ...@@ -278,6 +281,7 @@ public class WebServer implements Service {
"webSSL", false); "webSSL", false);
allowOthers = SortedProperties.getBooleanProperty(prop, allowOthers = SortedProperties.getBooleanProperty(prop,
"webAllowOthers", false); "webAllowOthers", false);
setAdminPassword(SortedProperties.getStringProperty(prop, "webAdminPassword", null));
commandHistoryString = prop.getProperty(COMMAND_HISTORY); commandHistoryString = prop.getProperty(COMMAND_HISTORY);
for (int i = 0; args != null && i < args.length; i++) { for (int i = 0; args != null && i < args.length; i++) {
String a = args[i]; String a = args[i];
...@@ -294,6 +298,10 @@ public class WebServer implements Service { ...@@ -294,6 +298,10 @@ public class WebServer implements Service {
SysProperties.setBaseDir(baseDir); SysProperties.setBaseDir(baseDir);
} else if (Tool.isOption(a, "-ifExists")) { } else if (Tool.isOption(a, "-ifExists")) {
ifExists = true; 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")) { } else if (Tool.isOption(a, "-properties")) {
// already set // already set
i++; i++;
...@@ -677,6 +685,9 @@ public class WebServer implements Service { ...@@ -677,6 +685,9 @@ public class WebServer implements Service {
Boolean.toString(SortedProperties.getBooleanProperty(old, "webAllowOthers", allowOthers))); Boolean.toString(SortedProperties.getBooleanProperty(old, "webAllowOthers", allowOthers)));
prop.setProperty("webSSL", prop.setProperty("webSSL",
Boolean.toString(SortedProperties.getBooleanProperty(old, "webSSL", ssl))); Boolean.toString(SortedProperties.getBooleanProperty(old, "webSSL", ssl)));
if (adminPassword != null) {
prop.setProperty("webAdminPassword", StringUtils.convertBytesToHex(adminPassword));
}
if (commandHistoryString != null) { if (commandHistoryString != null) {
prop.setProperty(COMMAND_HISTORY, commandHistoryString); prop.setProperty(COMMAND_HISTORY, commandHistoryString);
} }
...@@ -846,4 +857,36 @@ public class WebServer implements Service { ...@@ -846,4 +857,36 @@ public class WebServer implements Service {
return allowChunked; 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;
}
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));
}
} }
...@@ -98,9 +98,10 @@ class WebSession { ...@@ -98,9 +98,10 @@ class WebSession {
* Remove a session attribute from the map. * Remove a session attribute from the map.
* *
* @param key the key * @param key the key
* @return value that was associated with the key, or null
*/ */
void remove(String key) { Object remove(String key) {
map.remove(key); return map.remove(key);
} }
/** /**
......
...@@ -6,7 +6,7 @@ a.password=Пароль ...@@ -6,7 +6,7 @@ a.password=Пароль
a.remoteConnectionsDisabled=Извините, удаленные подключения ('webAllowOthers') запрещены на этом сервере. a.remoteConnectionsDisabled=Извините, удаленные подключения ('webAllowOthers') запрещены на этом сервере.
a.title=H2 Console a.title=H2 Console
a.tools=Инструменты a.tools=Инструменты
a.user=Пользователь Имя a.user=Имя пользователя
admin.executing=Выполняется admin.executing=Выполняется
admin.ip=IP admin.ip=IP
admin.lastAccess=Последний Вход admin.lastAccess=Последний Вход
...@@ -20,7 +20,7 @@ adminConnection=Безопасность подключения ...@@ -20,7 +20,7 @@ adminConnection=Безопасность подключения
adminHttp=Используйте незашифрованные HTTP-соединения adminHttp=Используйте незашифрованные HTTP-соединения
adminHttps=Используйте SSL (HTTPS) соединения adminHttps=Используйте SSL (HTTPS) соединения
adminLocal=Разрешены только локальные подключения adminLocal=Разрешены только локальные подключения
adminLogin=Администратор Логин adminLogin=Административный вход
adminLoginCancel=Отменить adminLoginCancel=Отменить
adminLoginOk=OK adminLoginOk=OK
adminLogout=Выход adminLogout=Выход
......
...@@ -15,7 +15,7 @@ Initial Developer: H2 Group ...@@ -15,7 +15,7 @@ Initial Developer: H2 Group
${text.adminTitle} ${text.adminTitle}
</h1> </h1>
<p> <p>
<a href="index.do?jsessionid=${sessionId}">${text.adminLogout}</a> <a href="logout.do?jsessionid=${sessionId}">${text.adminLogout}</a>
</p> </p>
<hr /> <hr />
<form name="admin" method="post" action="adminSave.do?jsessionid=${sessionId}"> <form name="admin" method="post" action="adminSave.do?jsessionid=${sessionId}">
......
...@@ -10,7 +10,7 @@ Initial Developer: H2 Group ...@@ -10,7 +10,7 @@ Initial Developer: H2 Group
<link rel="stylesheet" type="text/css" href="stylesheet.css" /> <link rel="stylesheet" type="text/css" href="stylesheet.css" />
</head> </head>
<body style="margin: 20px"> <body style="margin: 20px">
<form name="adminLogin" method="post" action="admin.do?jsessionid=${sessionId}"> <form name="adminLogin" method="post" action="adminLogin.do?jsessionid=${sessionId}">
<table class="login" cellspacing="0" cellpadding="0"> <table class="login" cellspacing="0" cellpadding="0">
<tr class="login"> <tr class="login">
<th class="login">${text.adminLogin}</th> <th class="login">${text.adminLogin}</th>
......
...@@ -72,7 +72,7 @@ public class Console extends Tool implements ShutdownHandler { ...@@ -72,7 +72,7 @@ public class Console extends Tool implements ShutdownHandler {
/** /**
* This tool starts the H2 Console (web-) server, as well as the TCP and PG * This tool starts the H2 Console (web-) server, as well as the TCP and PG
* server. For JDK 1.6, a system tray icon is created, for platforms that * server. A system tray icon is created, for platforms that
* support it. Otherwise, a small window opens. * support it. Otherwise, a small window opens.
* *
* @param args the command line arguments * @param args the command line arguments
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
package org.h2.tools; package org.h2.tools;
import java.awt.Button; import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.Frame; import java.awt.Frame;
...@@ -19,8 +20,10 @@ import java.awt.MenuItem; ...@@ -19,8 +20,10 @@ import java.awt.MenuItem;
import java.awt.Panel; import java.awt.Panel;
import java.awt.PopupMenu; import java.awt.PopupMenu;
import java.awt.SystemColor; import java.awt.SystemColor;
import java.awt.TextArea;
import java.awt.TextField; import java.awt.TextField;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
...@@ -28,6 +31,7 @@ import java.awt.event.MouseListener; ...@@ -28,6 +31,7 @@ import java.awt.event.MouseListener;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.awt.event.WindowListener; import java.awt.event.WindowListener;
import java.io.IOException; import java.io.IOException;
import java.sql.DriverManager;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
...@@ -39,11 +43,18 @@ import org.h2.util.Utils; ...@@ -39,11 +43,18 @@ import org.h2.util.Utils;
public class GUIConsole extends Console implements ActionListener, MouseListener, WindowListener { public class GUIConsole extends Console implements ActionListener, MouseListener, WindowListener {
private long lastOpenNs; private long lastOpenNs;
private Frame frame;
private boolean trayIconUsed; private boolean trayIconUsed;
private Font font; private Font font;
private Button startBrowser;
private Frame statusFrame;
private TextField urlText; private TextField urlText;
private Button startBrowser;
private Frame createFrame;
private TextField pathField, userField, passwordField;
private Button createButton;
private TextArea errorArea;
private Object tray; private Object tray;
private Object trayIcon; private Object trayIcon;
...@@ -53,7 +64,7 @@ public class GUIConsole extends Console implements ActionListener, MouseListener ...@@ -53,7 +64,7 @@ public class GUIConsole extends Console implements ActionListener, MouseListener
loadFont(); loadFont();
try { try {
if (!createTrayIcon()) { if (!createTrayIcon()) {
showWindow(); showStatusWindow();
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
...@@ -77,9 +88,9 @@ public class GUIConsole extends Console implements ActionListener, MouseListener ...@@ -77,9 +88,9 @@ public class GUIConsole extends Console implements ActionListener, MouseListener
@Override @Override
public void shutdown() { public void shutdown() {
super.shutdown(); super.shutdown();
if (frame != null) { if (statusFrame != null) {
frame.dispose(); statusFrame.dispose();
frame = null; statusFrame = null;
} }
if (trayIconUsed) { if (trayIconUsed) {
try { try {
...@@ -129,6 +140,11 @@ public class GUIConsole extends Console implements ActionListener, MouseListener ...@@ -129,6 +140,11 @@ public class GUIConsole extends Console implements ActionListener, MouseListener
itemConsole.addActionListener(this); itemConsole.addActionListener(this);
itemConsole.setFont(font); itemConsole.setFont(font);
menuConsole.add(itemConsole); menuConsole.add(itemConsole);
MenuItem itemCreate = new MenuItem("Create a new database...");
itemCreate.setActionCommand("showCreate");
itemCreate.addActionListener(this);
itemCreate.setFont(font);
menuConsole.add(itemCreate);
MenuItem itemStatus = new MenuItem("Status"); MenuItem itemStatus = new MenuItem("Status");
itemStatus.setActionCommand("status"); itemStatus.setActionCommand("status");
itemStatus.addActionListener(this); itemStatus.addActionListener(this);
...@@ -178,21 +194,21 @@ public class GUIConsole extends Console implements ActionListener, MouseListener ...@@ -178,21 +194,21 @@ public class GUIConsole extends Console implements ActionListener, MouseListener
} }
} }
private void showWindow() { private void showStatusWindow() {
if (frame != null) { if (statusFrame != null) {
return; return;
} }
frame = new Frame("H2 Console"); statusFrame = new Frame("H2 Console");
frame.addWindowListener(this); statusFrame.addWindowListener(this);
Image image = loadImage("/org/h2/res/h2.png"); Image image = loadImage("/org/h2/res/h2.png");
if (image != null) { if (image != null) {
frame.setIconImage(image); statusFrame.setIconImage(image);
} }
frame.setResizable(false); statusFrame.setResizable(false);
frame.setBackground(SystemColor.control); statusFrame.setBackground(SystemColor.control);
GridBagLayout layout = new GridBagLayout(); GridBagLayout layout = new GridBagLayout();
frame.setLayout(layout); statusFrame.setLayout(layout);
// the main panel keeps everything together // the main panel keeps everything together
Panel mainPanel = new Panel(layout); Panel mainPanel = new Panel(layout);
...@@ -242,15 +258,15 @@ public class GUIConsole extends Console implements ActionListener, MouseListener ...@@ -242,15 +258,15 @@ public class GUIConsole extends Console implements ActionListener, MouseListener
startBrowser.addActionListener(this); startBrowser.addActionListener(this);
startBrowser.setFont(font); startBrowser.setFont(font);
mainPanel.add(startBrowser, constraintsButton); mainPanel.add(startBrowser, constraintsButton);
frame.add(mainPanel, constraintsPanel); statusFrame.add(mainPanel, constraintsPanel);
int width = 300, height = 120; int width = 300, height = 120;
frame.setSize(width, height); statusFrame.setSize(width, height);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame.setLocation((screenSize.width - width) / 2, statusFrame.setLocation((screenSize.width - width) / 2,
(screenSize.height - height) / 2); (screenSize.height - height) / 2);
try { try {
frame.setVisible(true); statusFrame.setVisible(true);
} catch (Throwable t) { } catch (Throwable t) {
// ignore // ignore
// some systems don't support this method, for example IKVM // some systems don't support this method, for example IKVM
...@@ -258,8 +274,8 @@ public class GUIConsole extends Console implements ActionListener, MouseListener ...@@ -258,8 +274,8 @@ public class GUIConsole extends Console implements ActionListener, MouseListener
} }
try { try {
// ensure this window is in front of the browser // ensure this window is in front of the browser
frame.setAlwaysOnTop(true); statusFrame.setAlwaysOnTop(true);
frame.setAlwaysOnTop(false); statusFrame.setAlwaysOnTop(false);
} catch (Throwable t) { } catch (Throwable t) {
// ignore // ignore
} }
...@@ -279,6 +295,161 @@ public class GUIConsole extends Console implements ActionListener, MouseListener ...@@ -279,6 +295,161 @@ public class GUIConsole extends Console implements ActionListener, MouseListener
} }
} }
private void showCreateDatabase() {
if (createFrame != null) {
return;
}
createFrame = new Frame("H2 Console");
createFrame.addWindowListener(this);
Image image = loadImage("/org/h2/res/h2.png");
if (image != null) {
createFrame.setIconImage(image);
}
createFrame.setResizable(false);
createFrame.setBackground(SystemColor.control);
GridBagLayout layout = new GridBagLayout();
createFrame.setLayout(layout);
// the main panel keeps everything together
Panel mainPanel = new Panel(layout);
GridBagConstraints constraints;
constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = 0;
Label urlLabel = new Label("Database path:", Label.LEFT);
urlLabel.setFont(font);
mainPanel.add(urlLabel, constraints);
constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridy = 0;
constraints.weightx = 1d;
constraints.insets = new Insets(0, 5, 0, 0);
constraints.gridx = 1;
pathField = new TextField();
pathField.setFont(font);
pathField.setText("./test");
mainPanel.add(pathField, constraints);
constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = 1;
Label userLabel = new Label("Username:", Label.LEFT);
userLabel.setFont(font);
mainPanel.add(userLabel, constraints);
constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridy = 1;
constraints.weightx = 1d;
constraints.insets = new Insets(0, 5, 0, 0);
constraints.gridx = 1;
userField = new TextField();
userField.setFont(font);
userField.setText("sa");
mainPanel.add(userField, constraints);
constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = 2;
Label passwordLabel = new Label("Password:", Label.LEFT);
passwordLabel.setFont(font);
mainPanel.add(passwordLabel, constraints);
constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridy = 2;
constraints.weightx = 1d;
constraints.insets = new Insets(0, 5, 0, 0);
constraints.gridx = 1;
passwordField = new TextField();
passwordField.setFont(font);
mainPanel.add(passwordField, constraints);
constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridwidth = 2;
constraints.insets = new Insets(10, 0, 0, 0);
constraints.gridy = 3;
constraints.anchor = GridBagConstraints.EAST;
createButton = new Button("Create");
createButton.setFocusable(false);
createButton.setActionCommand("create");
createButton.addActionListener(this);
createButton.setFont(font);
mainPanel.add(createButton, constraints);
constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridy = 4;
constraints.weightx = 1d;
constraints.insets = new Insets(10, 0, 0, 0);
constraints.gridx = 0;
constraints.gridwidth = 2;
errorArea = new TextArea();
errorArea.setFont(font);
errorArea.setEditable(false);
mainPanel.add(errorArea, constraints);
constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.weightx = 1d;
constraints.weighty = 1d;
constraints.fill = GridBagConstraints.BOTH;
constraints.insets = new Insets(0, 10, 0, 10);
constraints.gridy = 0;
createFrame.add(mainPanel, constraints);
int width = 300, height = 300;
createFrame.setSize(width, height);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
createFrame.setLocation((screenSize.width - width) / 2,
(screenSize.height - height) / 2);
try {
createFrame.setVisible(true);
} catch (Throwable t) {
// ignore
// some systems don't support this method, for example IKVM
// however it still works
}
try {
// ensure this window is in front of the browser
createFrame.setAlwaysOnTop(true);
createFrame.setAlwaysOnTop(false);
} catch (Throwable t) {
// ignore
}
}
private void createDatabase() {
if (web == null || createFrame == null) {
return;
}
String path = pathField.getText(), user = userField.getText(), password = passwordField.getText();
if (password.isEmpty()) {
errorArea.setForeground(Color.RED);
errorArea.setText("Specify a password");
return;
}
String url = "jdbc:h2:" + path;
try {
DriverManager.getConnection(url, user, password).close();
errorArea.setForeground(new Color(0, 0x99, 0));
errorArea.setText("Database was created successfully.\n\n"
+ "JDBC URL for H2 Console:\n"
+ url);
} catch (Exception ex) {
errorArea.setForeground(Color.RED);
errorArea.setText(ex.getMessage());
}
}
/** /**
* INTERNAL * INTERNAL
*/ */
...@@ -289,11 +460,17 @@ public class GUIConsole extends Console implements ActionListener, MouseListener ...@@ -289,11 +460,17 @@ public class GUIConsole extends Console implements ActionListener, MouseListener
shutdown(); shutdown();
} else if ("console".equals(command)) { } else if ("console".equals(command)) {
startBrowser(); startBrowser();
} else if ("showCreate".equals(command)) {
showCreateDatabase();
} else if ("status".equals(command)) { } else if ("status".equals(command)) {
showWindow(); showStatusWindow();
} else if (startBrowser == e.getSource()) { } else {
// for some reason, IKVM ignores setActionCommand // for some reason, IKVM ignores setActionCommand
startBrowser(); if (startBrowser == e.getSource()) {
startBrowser();
} else if (createButton == e.getSource()) {
createDatabase();
}
} }
} }
...@@ -345,8 +522,14 @@ public class GUIConsole extends Console implements ActionListener, MouseListener ...@@ -345,8 +522,14 @@ public class GUIConsole extends Console implements ActionListener, MouseListener
@Override @Override
public void windowClosing(WindowEvent e) { public void windowClosing(WindowEvent e) {
if (trayIconUsed) { if (trayIconUsed) {
frame.dispose(); Window window = e.getWindow();
frame = null; if (window == statusFrame) {
statusFrame.dispose();
statusFrame = null;
} else if (window == createFrame) {
createFrame.dispose();
createFrame = null;
}
} else { } else {
shutdown(); shutdown();
} }
......
...@@ -70,6 +70,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -70,6 +70,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
* <td>The port (default: 8082)</td></tr> * <td>The port (default: 8082)</td></tr>
* <tr><td>[-webSSL]</td> * <tr><td>[-webSSL]</td>
* <td>Use encrypted (HTTPS) connections</td></tr> * <td>Use encrypted (HTTPS) connections</td></tr>
* <tr><td>[-webAdminPassword]</td>
* <td>Password of DB Console administrator</td></tr>
* <tr><td>[-browser]</td> * <tr><td>[-browser]</td>
* <td>Start a browser connecting to the web server</td></tr> * <td>Start a browser connecting to the web server</td></tr>
* <tr><td>[-tcp]</td> * <tr><td>[-tcp]</td>
...@@ -102,6 +104,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -102,6 +104,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
* <td>The base directory for H2 databases (all servers)</td></tr> * <td>The base directory for H2 databases (all servers)</td></tr>
* <tr><td>[-ifExists]</td> * <tr><td>[-ifExists]</td>
* <td>Only existing databases may be opened (all servers)</td></tr> * <td>Only existing databases may be opened (all servers)</td></tr>
* <tr><td>[-ifNotExists]</td>
* <td>Databases are created when accessed</td></tr>
* <tr><td>[-trace]</td> * <tr><td>[-trace]</td>
* <td>Print additional trace information (all servers)</td></tr> * <td>Print additional trace information (all servers)</td></tr>
* <tr><td>[-key &lt;from&gt; &lt;to&gt;]</td> * <tr><td>[-key &lt;from&gt; &lt;to&gt;]</td>
...@@ -135,6 +139,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -135,6 +139,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
// no parameters // no parameters
} else if ("-webPort".equals(arg)) { } else if ("-webPort".equals(arg)) {
i++; i++;
} else if ("-webAdminPassword".equals(arg)) {
i += 2;
} else { } else {
throwUnsupportedOption(arg); throwUnsupportedOption(arg);
} }
...@@ -194,6 +200,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -194,6 +200,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
// no parameters // no parameters
} else if ("-ifExists".equals(arg)) { } else if ("-ifExists".equals(arg)) {
// no parameters // no parameters
} else if ("-ifNotExists".equals(arg)) {
// no parameters
} else if ("-baseDir".equals(arg)) { } else if ("-baseDir".equals(arg)) {
i++; i++;
} else if ("-key".equals(arg)) { } else if ("-key".equals(arg)) {
...@@ -232,6 +240,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -232,6 +240,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
// no parameters // no parameters
} else if ("-webPort".equals(arg)) { } else if ("-webPort".equals(arg)) {
i++; i++;
} else if ("-webAdminPassword".equals(arg)) {
i += 2;
} else { } else {
showUsageAndThrowUnsupportedOption(arg); showUsageAndThrowUnsupportedOption(arg);
} }
...@@ -280,6 +290,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -280,6 +290,8 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
// no parameters // no parameters
} else if ("-ifExists".equals(arg)) { } else if ("-ifExists".equals(arg)) {
// no parameters // no parameters
} else if ("-ifNotExists".equals(arg)) {
// no parameters
} else if ("-baseDir".equals(arg)) { } else if ("-baseDir".equals(arg)) {
i++; i++;
} else if ("-key".equals(arg)) { } else if ("-key".equals(arg)) {
...@@ -407,7 +419,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -407,7 +419,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
* </pre> * </pre>
* Supported options are: * Supported options are:
* -webPort, -webSSL, -webAllowOthers, -webDaemon, * -webPort, -webSSL, -webAllowOthers, -webDaemon,
* -trace, -ifExists, -baseDir, -properties. * -trace, -ifExists, -ifNotExists, -baseDir, -properties.
* See the main method for details. * See the main method for details.
* *
* @param args the argument list * @param args the argument list
...@@ -429,7 +441,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -429,7 +441,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
* </pre> * </pre>
* Supported options are: * Supported options are:
* -tcpPort, -tcpSSL, -tcpPassword, -tcpAllowOthers, -tcpDaemon, * -tcpPort, -tcpSSL, -tcpPassword, -tcpAllowOthers, -tcpDaemon,
* -trace, -ifExists, -baseDir, -key. * -trace, -ifExists, -ifNotExists, -baseDir, -key.
* See the main method for details. * See the main method for details.
* <p> * <p>
* If no port is specified, the default port is used if possible, * If no port is specified, the default port is used if possible,
...@@ -456,7 +468,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -456,7 +468,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
* </pre> * </pre>
* Supported options are: * Supported options are:
* -pgPort, -pgAllowOthers, -pgDaemon, * -pgPort, -pgAllowOthers, -pgDaemon,
* -trace, -ifExists, -baseDir, -key. * -trace, -ifExists, -ifNotExists, -baseDir, -key.
* See the main method for details. * See the main method for details.
* <p> * <p>
* If no port is specified, the default port is used if possible, * If no port is specified, the default port is used if possible,
......
...@@ -78,6 +78,18 @@ public class SortedProperties extends Properties { ...@@ -78,6 +78,18 @@ public class SortedProperties extends Properties {
} }
} }
/**
* Get a string property value from a properties object.
*
* @param prop the properties object
* @param key the key
* @param def the default value
* @return the value if set, or the default value if not
*/
public static String getStringProperty(Properties prop, String key, String def) {
return prop.getProperty(key, def);
}
/** /**
* Load a properties object from a file. * Load a properties object from a file.
* *
......
...@@ -1094,7 +1094,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -1094,7 +1094,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
DeleteDbFiles.execute(TestBase.BASE_TEST_DIR, null, true); DeleteDbFiles.execute(TestBase.BASE_TEST_DIR, null, true);
FileUtils.deleteRecursive("trace.db", false); FileUtils.deleteRecursive("trace.db", false);
if (networked) { if (networked) {
String[] args = ssl ? new String[] { "-tcpSSL" } : new String[0]; String[] args = ssl ? new String[] { "-ifNotExists", "-tcpSSL" } : new String[] { "-ifNotExists" };
server = Server.createTcpServer(args); server = Server.createTcpServer(args);
try { try {
server.start(); server.start();
......
...@@ -62,7 +62,7 @@ public class TestCluster extends TestDb { ...@@ -62,7 +62,7 @@ public class TestCluster extends TestDb {
Connection conn; Connection conn;
Statement stat; Statement stat;
Server n1 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node1").start(); Server n1 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node1").start();
int port1 = n1.getPort(); int port1 = n1.getPort();
String url1 = getURL("jdbc:h2:tcp://localhost:" + port1 + "/test", false); String url1 = getURL("jdbc:h2:tcp://localhost:" + port1 + "/test", false);
...@@ -72,7 +72,7 @@ public class TestCluster extends TestDb { ...@@ -72,7 +72,7 @@ public class TestCluster extends TestDb {
stat.execute("insert into t1 values(1, repeat('Hello', 50))"); stat.execute("insert into t1 values(1, repeat('Hello', 50))");
conn.close(); conn.close();
Server n2 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node2").start(); Server n2 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node2").start();
int port2 = n2.getPort(); int port2 = n2.getPort();
String url2 = getURL("jdbc:h2:tcp://localhost:" + port2 + "/test", false); String url2 = getURL("jdbc:h2:tcp://localhost:" + port2 + "/test", false);
...@@ -100,9 +100,11 @@ public class TestCluster extends TestDb { ...@@ -100,9 +100,11 @@ public class TestCluster extends TestDb {
ResultSet rs; ResultSet rs;
Server server1 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node1").start(); Server server1 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node1")
.start();
int port1 = server1.getPort(); int port1 = server1.getPort();
Server server2 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node2").start(); Server server2 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node2")
.start();
int port2 = server2.getPort(); int port2 = server2.getPort();
String url1 = getURL("jdbc:h2:tcp://localhost:" + port1 + "/test", true); String url1 = getURL("jdbc:h2:tcp://localhost:" + port1 + "/test", true);
...@@ -130,7 +132,7 @@ public class TestCluster extends TestDb { ...@@ -130,7 +132,7 @@ public class TestCluster extends TestDb {
rs.next(); rs.next();
assertEquals(5, rs.getInt(1)); assertEquals(5, rs.getInt(1));
server2 = org.h2.tools.Server.createTcpServer("-tcpPort", server2 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-tcpPort",
"" + port2 , "-baseDir", getBaseDir() + "/node2").start(); "" + port2 , "-baseDir", getBaseDir() + "/node2").start();
CreateCluster.main("-urlSource", url1, "-urlTarget", url2, CreateCluster.main("-urlSource", url1, "-urlTarget", url2,
"-user", user, "-password", password, "-serverList", "-user", user, "-password", password, "-serverList",
...@@ -159,9 +161,9 @@ public class TestCluster extends TestDb { ...@@ -159,9 +161,9 @@ public class TestCluster extends TestDb {
Statement stat; Statement stat;
ResultSet rs; ResultSet rs;
Server n1 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node1").start(); Server n1 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node1").start();
int port1 = n1.getPort(); int port1 = n1.getPort();
Server n2 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node2").start(); Server n2 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node2").start();
int port2 = n2.getPort(); int port2 = n2.getPort();
String url1 = getURL("jdbc:h2:tcp://localhost:" + port1 + "/test", true); String url1 = getURL("jdbc:h2:tcp://localhost:" + port1 + "/test", true);
...@@ -204,9 +206,9 @@ public class TestCluster extends TestDb { ...@@ -204,9 +206,9 @@ public class TestCluster extends TestDb {
ResultSet rs; ResultSet rs;
Server n1 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node1").start(); Server n1 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node1").start();
int port1 = n1.getPort(); int port1 = n1.getPort();
Server n2 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node2").start(); Server n2 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node2").start();
int port2 = n2.getPort(); int port2 = n2.getPort();
String serverList = "localhost:" + port1 + ",localhost:" + port2; String serverList = "localhost:" + port1 + ",localhost:" + port2;
String url1 = getURL("jdbc:h2:tcp://localhost:" + port1 + "/test", true); String url1 = getURL("jdbc:h2:tcp://localhost:" + port1 + "/test", true);
...@@ -255,9 +257,9 @@ public class TestCluster extends TestDb { ...@@ -255,9 +257,9 @@ public class TestCluster extends TestDb {
Connection conn; Connection conn;
Server n1 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node1").start(); Server n1 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node1").start();
int port1 = n1.getPort(); int port1 = n1.getPort();
Server n2 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node2").start(); Server n2 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node2").start();
int port2 = n2.getPort(); int port2 = n2.getPort();
String serverList = "localhost:" + port1 + ",localhost:" + port2; String serverList = "localhost:" + port1 + ",localhost:" + port2;
...@@ -306,7 +308,7 @@ public class TestCluster extends TestDb { ...@@ -306,7 +308,7 @@ public class TestCluster extends TestDb {
int len = 10; int len = 10;
// initialize the database // initialize the database
Server n1 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node1").start(); Server n1 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node1").start();
int port1 = n1.getPort(); int port1 = n1.getPort();
String url1 = getURL("jdbc:h2:tcp://localhost:" + port1 + "/test", false); String url1 = getURL("jdbc:h2:tcp://localhost:" + port1 + "/test", false);
conn = getConnection(url1, user, password); conn = getConnection(url1, user, password);
...@@ -317,7 +319,7 @@ public class TestCluster extends TestDb { ...@@ -317,7 +319,7 @@ public class TestCluster extends TestDb {
stat.execute("grant all on test to test"); stat.execute("grant all on test to test");
// start the second server // start the second server
Server n2 = org.h2.tools.Server.createTcpServer("-baseDir", getBaseDir() + "/node2").start(); Server n2 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-baseDir", getBaseDir() + "/node2").start();
int port2 = n2.getPort(); int port2 = n2.getPort();
String url2 = getURL("jdbc:h2:tcp://localhost:" + port2 + "/test", false); String url2 = getURL("jdbc:h2:tcp://localhost:" + port2 + "/test", false);
...@@ -352,7 +354,7 @@ public class TestCluster extends TestDb { ...@@ -352,7 +354,7 @@ public class TestCluster extends TestDb {
connApp.setAutoCommit(true); connApp.setAutoCommit(true);
// re-create the cluster // re-create the cluster
n2 = org.h2.tools.Server.createTcpServer("-tcpPort", "" + port2, n2 = org.h2.tools.Server.createTcpServer("-ifNotExists", "-tcpPort", "" + port2,
"-baseDir", getBaseDir() + "/node2").start(); "-baseDir", getBaseDir() + "/node2").start();
CreateCluster.main("-urlSource", url1, "-urlTarget", url2, CreateCluster.main("-urlSource", url1, "-urlTarget", url2,
"-user", user, "-password", password, "-serverList", "-user", user, "-password", password, "-serverList",
......
...@@ -172,7 +172,7 @@ public class TestWeb extends TestDb { ...@@ -172,7 +172,7 @@ public class TestWeb extends TestDb {
Server server = new Server(); Server server = new Server();
server.setOut(new PrintStream(new ByteArrayOutputStream())); server.setOut(new PrintStream(new ByteArrayOutputStream()));
server.runTool("-web", "-webPort", "8182", server.runTool("-web", "-webPort", "8182",
"-properties", "null", "-tcp", "-tcpPort", "9101"); "-properties", "null", "-tcp", "-tcpPort", "9101", "-webAdminPassword", "123");
try { try {
String url = "http://localhost:8182"; String url = "http://localhost:8182";
WebClient client; WebClient client;
...@@ -180,6 +180,7 @@ public class TestWeb extends TestDb { ...@@ -180,6 +180,7 @@ public class TestWeb extends TestDb {
client = new WebClient(); client = new WebClient();
result = client.get(url); result = client.get(url);
client.readSessionId(result); client.readSessionId(result);
result = client.get(url, "adminLogin.do?password=123");
result = client.get(url, "tools.jsp"); result = client.get(url, "tools.jsp");
FileUtils.delete(getBaseDir() + "/backup.zip"); FileUtils.delete(getBaseDir() + "/backup.zip");
result = client.get(url, "tools.do?tool=Backup&args=-dir," + result = client.get(url, "tools.do?tool=Backup&args=-dir," +
...@@ -264,7 +265,8 @@ public class TestWeb extends TestDb { ...@@ -264,7 +265,8 @@ public class TestWeb extends TestDb {
getUser(), getPassword()); getUser(), getPassword());
Server server = new Server(); Server server = new Server();
server.setOut(new PrintStream(new ByteArrayOutputStream())); server.setOut(new PrintStream(new ByteArrayOutputStream()));
server.runTool("-ifExists", "-web", "-webPort", "8182", // -ifExists is the default
server.runTool("-web", "-webPort", "8182",
"-properties", "null", "-tcp", "-tcpPort", "9101"); "-properties", "null", "-tcp", "-tcpPort", "9101");
try { try {
String url = "http://localhost:8182"; String url = "http://localhost:8182";
...@@ -288,12 +290,13 @@ public class TestWeb extends TestDb { ...@@ -288,12 +290,13 @@ public class TestWeb extends TestDb {
server.shutdown(); server.shutdown();
conn.close(); conn.close();
} }
} }
private void testWebApp() throws Exception { private void testWebApp() throws Exception {
Server server = new Server(); Server server = new Server();
server.setOut(new PrintStream(new ByteArrayOutputStream())); server.setOut(new PrintStream(new ByteArrayOutputStream()));
server.runTool("-web", "-webPort", "8182", server.runTool("-ifNotExists", "-web", "-webPort", "8182",
"-properties", "null", "-tcp", "-tcpPort", "9101"); "-properties", "null", "-tcp", "-tcpPort", "9101");
try { try {
String url = "http://localhost:8182"; String url = "http://localhost:8182";
......
...@@ -94,7 +94,7 @@ public class TestAutoReconnect extends TestDb { ...@@ -94,7 +94,7 @@ public class TestAutoReconnect extends TestDb {
"AUTO_SERVER=TRUE;OPEN_NEW=TRUE"; "AUTO_SERVER=TRUE;OPEN_NEW=TRUE";
restart(); restart();
} else { } else {
server = Server.createTcpServer().start(); server = Server.createTcpServer("-ifNotExists").start();
int port = server.getPort(); int port = server.getPort();
url = "jdbc:h2:tcp://localhost:" + port + "/" + getBaseDir() + "/" + getTestName() + ";" + url = "jdbc:h2:tcp://localhost:" + port + "/" + getBaseDir() + "/" + getTestName() + ";" +
"FILE_LOCK=SOCKET;AUTO_RECONNECT=TRUE"; "FILE_LOCK=SOCKET;AUTO_RECONNECT=TRUE";
......
...@@ -77,7 +77,7 @@ public class TestPgServer extends TestDb { ...@@ -77,7 +77,7 @@ public class TestPgServer extends TestDb {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("create table test(id int, name varchar(255))"); stat.execute("create table test(id int, name varchar(255))");
Server server = createPgServer("-baseDir", getBaseDir(), Server server = createPgServer("-baseDir", getBaseDir(),
"-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "-ifNotExists", "-pgPort", "5535", "-pgDaemon", "-key", "pgserver",
"mem:pgserver"); "mem:pgserver");
try { try {
Connection conn2; Connection conn2;
...@@ -128,7 +128,7 @@ public class TestPgServer extends TestDb { ...@@ -128,7 +128,7 @@ public class TestPgServer extends TestDb {
private void testPgAdapter() throws SQLException { private void testPgAdapter() throws SQLException {
deleteDb("pgserver"); deleteDb("pgserver");
Server server = Server.createPgServer( Server server = Server.createPgServer(
"-baseDir", getBaseDir(), "-pgPort", "5535", "-pgDaemon"); "-ifNotExists", "-baseDir", getBaseDir(), "-pgPort", "5535", "-pgDaemon");
assertEquals(5535, server.getPort()); assertEquals(5535, server.getPort());
assertEquals("Not started", server.getStatus()); assertEquals("Not started", server.getStatus());
server.start(); server.start();
...@@ -148,7 +148,7 @@ public class TestPgServer extends TestDb { ...@@ -148,7 +148,7 @@ public class TestPgServer extends TestDb {
} }
Server server = createPgServer( Server server = createPgServer(
"-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver"); "-ifNotExists", "-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver");
ExecutorService executor = Executors.newSingleThreadExecutor(); ExecutorService executor = Executors.newSingleThreadExecutor();
try { try {
...@@ -374,7 +374,7 @@ public class TestPgServer extends TestDb { ...@@ -374,7 +374,7 @@ public class TestPgServer extends TestDb {
return; return;
} }
Server server = createPgServer( Server server = createPgServer(
"-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver"); "-ifNotExists", "-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver");
try { try {
Connection conn = DriverManager.getConnection( Connection conn = DriverManager.getConnection(
"jdbc:postgresql://localhost:5535/pgserver", "sa", "sa"); "jdbc:postgresql://localhost:5535/pgserver", "sa", "sa");
...@@ -401,7 +401,7 @@ public class TestPgServer extends TestDb { ...@@ -401,7 +401,7 @@ public class TestPgServer extends TestDb {
} }
Server server = createPgServer( Server server = createPgServer(
"-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver"); "-ifNotExists", "-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver");
try { try {
Properties props = new Properties(); Properties props = new Properties();
props.setProperty("user", "sa"); props.setProperty("user", "sa");
...@@ -476,7 +476,7 @@ public class TestPgServer extends TestDb { ...@@ -476,7 +476,7 @@ public class TestPgServer extends TestDb {
} }
Server server = createPgServer( Server server = createPgServer(
"-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver"); "-ifNotExists", "-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver");
try { try {
Properties props = new Properties(); Properties props = new Properties();
props.setProperty("user", "sa"); props.setProperty("user", "sa");
...@@ -535,7 +535,7 @@ public class TestPgServer extends TestDb { ...@@ -535,7 +535,7 @@ public class TestPgServer extends TestDb {
} }
Server server = createPgServer( Server server = createPgServer(
"-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver"); "-ifNotExists", "-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver");
try { try {
Properties props = new Properties(); Properties props = new Properties();
......
...@@ -592,7 +592,7 @@ public class TestTools extends TestDb { ...@@ -592,7 +592,7 @@ public class TestTools extends TestDb {
result = runServer(1, new String[]{"-xy"}); result = runServer(1, new String[]{"-xy"});
assertContains(result, "Starts the H2 Console"); assertContains(result, "Starts the H2 Console");
assertContains(result, "Feature not supported"); assertContains(result, "Feature not supported");
result = runServer(0, new String[]{"-tcp", result = runServer(0, new String[]{"-ifNotExists", "-tcp",
"-tcpPort", "9001", "-tcpPassword", "abc"}); "-tcpPort", "9001", "-tcpPassword", "abc"});
assertContains(result, "tcp://"); assertContains(result, "tcp://");
assertContains(result, ":9001"); assertContains(result, ":9001");
...@@ -613,7 +613,7 @@ public class TestTools extends TestDb { ...@@ -613,7 +613,7 @@ public class TestTools extends TestDb {
Connection conn; Connection conn;
try { try {
result = runServer(0, new String[]{"-tcp", result = runServer(0, new String[]{"-ifNotExists", "-tcp",
"-tcpAllowOthers", "-tcpPort", "9001", "-tcpPassword", "abcdef", "-tcpSSL"}); "-tcpAllowOthers", "-tcpPort", "9001", "-tcpPassword", "abcdef", "-tcpSSL"});
assertContains(result, "ssl://"); assertContains(result, "ssl://");
assertContains(result, ":9001"); assertContains(result, ":9001");
...@@ -629,7 +629,7 @@ public class TestTools extends TestDb { ...@@ -629,7 +629,7 @@ public class TestTools extends TestDb {
getConnection("jdbc:h2:ssl://localhost:9001/mem:", "sa", "sa"); getConnection("jdbc:h2:ssl://localhost:9001/mem:", "sa", "sa");
result = runServer(0, new String[]{ result = runServer(0, new String[]{
"-web", "-webPort", "9002", "-webAllowOthers", "-webSSL", "-ifNotExists", "-web", "-webPort", "9002", "-webAllowOthers", "-webSSL",
"-pg", "-pgAllowOthers", "-pgPort", "9003", "-pg", "-pgAllowOthers", "-pgPort", "9003",
"-tcp", "-tcpAllowOthers", "-tcpPort", "9006", "-tcpPassword", "abc"}); "-tcp", "-tcpAllowOthers", "-tcpPort", "9006", "-tcpPassword", "abc"});
Server stop = server; Server stop = server;
...@@ -1096,7 +1096,7 @@ public class TestTools extends TestDb { ...@@ -1096,7 +1096,7 @@ public class TestTools extends TestDb {
Connection conn; Connection conn;
try { try {
deleteDb("test"); deleteDb("test");
Server tcpServer = Server.createTcpServer( Server tcpServer = Server.createTcpServer("-ifNotExists",
"-baseDir", getBaseDir(), "-baseDir", getBaseDir(),
"-tcpAllowOthers").start(); "-tcpAllowOthers").start();
remainingServers.add(tcpServer); remainingServers.add(tcpServer);
...@@ -1150,7 +1150,7 @@ public class TestTools extends TestDb { ...@@ -1150,7 +1150,7 @@ public class TestTools extends TestDb {
JdbcUtils.closeSilently(conn); JdbcUtils.closeSilently(conn);
// Test filesystem prefix and escape from baseDir // Test filesystem prefix and escape from baseDir
deleteDb("testSplit"); deleteDb("testSplit");
server = Server.createTcpServer( server = Server.createTcpServer("-ifNotExists",
"-baseDir", getBaseDir(), "-baseDir", getBaseDir(),
"-tcpAllowOthers").start(); "-tcpAllowOthers").start();
final int p = server.getPort(); final int p = server.getPort();
......
...@@ -806,4 +806,4 @@ econd irst bcef ordinality nord unnest ...@@ -806,4 +806,4 @@ econd irst bcef ordinality nord unnest
analyst occupation distributive josaph aor engineer sajeewa isuru randil kevin doctor businessman artist ashan analyst occupation distributive josaph aor engineer sajeewa isuru randil kevin doctor businessman artist ashan
corrupts splitted disruption unintentional octets preconditions predicates subq objectweb insn opcodes corrupts splitted disruption unintentional octets preconditions predicates subq objectweb insn opcodes
preserves masking holder unboxing avert iae transformed subtle reevaluate exclusions subclause ftbl rgr preserves masking holder unboxing avert iae transformed subtle reevaluate exclusions subclause ftbl rgr
presorted inclusion contexts aax mwd percentile cont interpolate mwa hypothetical regproc childed listagg presorted inclusion contexts aax mwd percentile cont interpolate mwa hypothetical regproc childed listagg foreground
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论