/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.tools;

import java.net.URI;
import java.sql.Connection;
import java.sql.SQLException;

import org.h2.api.ErrorCode;
import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.server.Service;
import org.h2.server.ShutdownHandler;
import org.h2.server.TcpServer;
import org.h2.server.pg.PgServer;
import org.h2.server.web.WebServer;
import org.h2.util.StringUtils;
import org.h2.util.Tool;
import org.h2.util.Utils;

Starts the H2 Console (web-) server, TCP, and PG server.
@h2.resource
/** * Starts the H2 Console (web-) server, TCP, and PG server. * @h2.resource */
public class Server extends Tool implements Runnable, ShutdownHandler { private final Service service; private Server web, tcp, pg; private ShutdownHandler shutdownHandler; private boolean started; public Server() { // nothing to do this.service = null; }
Create a new server for the given service.
Params:
  • service – the service
  • args – the command line arguments
/** * Create a new server for the given service. * * @param service the service * @param args the command line arguments */
public Server(Service service, String... args) throws SQLException { verifyArgs(args); this.service = service; try { service.init(args); } catch (Exception e) { throw DbException.toSQLException(e); } }
When running without options, -tcp, -web, -browser and -pg are started.
Options are case sensitive. Supported options are:
[-help] or [-?] Print the list of options
[-web] Start the web server with the H2 Console
[-webAllowOthers] Allow other computers to connect - see below
[-webDaemon] Use a daemon thread
[-webPort <port>] The port (default: 8082)
[-webSSL] Use encrypted (HTTPS) connections
[-webAdminPassword] Password of DB Console administrator
[-browser] Start a browser connecting to the web server
[-tcp] Start the TCP server
[-tcpAllowOthers] Allow other computers to connect - see below
[-tcpDaemon] Use a daemon thread
[-tcpPort <port>] The port (default: 9092)
[-tcpSSL] Use encrypted (SSL) connections
[-tcpPassword <pwd>] The password for shutting down a TCP server
[-tcpShutdown "<url>"] Stop the TCP server; example: tcp://localhost
[-tcpShutdownForce] Do not wait until all connections are closed
[-pg] Start the PG server
[-pgAllowOthers] Allow other computers to connect - see below
[-pgDaemon] Use a daemon thread
[-pgPort <port>] The port (default: 5435)
[-properties "<dir>"] Server properties (default: ~, disable: null)
[-baseDir <dir>] The base directory for H2 databases (all servers)
[-ifExists] Only existing databases may be opened (all servers)
[-ifNotExists] Databases are created when accessed
[-trace] Print additional trace information (all servers)
[-key <from> <to>] Allows to map a database name to another (all servers)
The options -xAllowOthers are potentially risky.
For details, see Advanced Topics / Protection against Remote Access.
Params:
  • args – the command line arguments
@h2.resource
/** * When running without options, -tcp, -web, -browser and -pg are started. * <br /> * Options are case sensitive. Supported options are: * <table> * <tr><td>[-help] or [-?]</td> * <td>Print the list of options</td></tr> * <tr><td>[-web]</td> * <td>Start the web server with the H2 Console</td></tr> * <tr><td>[-webAllowOthers]</td> * <td>Allow other computers to connect - see below</td></tr> * <tr><td>[-webDaemon]</td> * <td>Use a daemon thread</td></tr> * <tr><td>[-webPort &lt;port&gt;]</td> * <td>The port (default: 8082)</td></tr> * <tr><td>[-webSSL]</td> * <td>Use encrypted (HTTPS) connections</td></tr> * <tr><td>[-webAdminPassword]</td> * <td>Password of DB Console administrator</td></tr> * <tr><td>[-browser]</td> * <td>Start a browser connecting to the web server</td></tr> * <tr><td>[-tcp]</td> * <td>Start the TCP server</td></tr> * <tr><td>[-tcpAllowOthers]</td> * <td>Allow other computers to connect - see below</td></tr> * <tr><td>[-tcpDaemon]</td> * <td>Use a daemon thread</td></tr> * <tr><td>[-tcpPort &lt;port&gt;]</td> * <td>The port (default: 9092)</td></tr> * <tr><td>[-tcpSSL]</td> * <td>Use encrypted (SSL) connections</td></tr> * <tr><td>[-tcpPassword &lt;pwd&gt;]</td> * <td>The password for shutting down a TCP server</td></tr> * <tr><td>[-tcpShutdown "&lt;url&gt;"]</td> * <td>Stop the TCP server; example: tcp://localhost</td></tr> * <tr><td>[-tcpShutdownForce]</td> * <td>Do not wait until all connections are closed</td></tr> * <tr><td>[-pg]</td> * <td>Start the PG server</td></tr> * <tr><td>[-pgAllowOthers]</td> * <td>Allow other computers to connect - see below</td></tr> * <tr><td>[-pgDaemon]</td> * <td>Use a daemon thread</td></tr> * <tr><td>[-pgPort &lt;port&gt;]</td> * <td>The port (default: 5435)</td></tr> * <tr><td>[-properties "&lt;dir&gt;"]</td> * <td>Server properties (default: ~, disable: null)</td></tr> * <tr><td>[-baseDir &lt;dir&gt;]</td> * <td>The base directory for H2 databases (all servers)</td></tr> * <tr><td>[-ifExists]</td> * <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> * <td>Print additional trace information (all servers)</td></tr> * <tr><td>[-key &lt;from&gt; &lt;to&gt;]</td> * <td>Allows to map a database name to another (all servers)</td></tr> * </table> * The options -xAllowOthers are potentially risky. * <br /> * For details, see Advanced Topics / Protection against Remote Access. * @h2.resource * * @param args the command line arguments */
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]; if (arg == null) { } else if ("-?".equals(arg) || "-help".equals(arg)) { // ok } else if (arg.startsWith("-web")) { if ("-web".equals(arg)) { // ok } else if ("-webAllowOthers".equals(arg)) { // no parameters } else if ("-webDaemon".equals(arg)) { // no parameters } else if ("-webSSL".equals(arg)) { // no parameters } else if ("-webPort".equals(arg)) { i++; } else if ("-webAdminPassword".equals(arg)) { i += 2; } else { throwUnsupportedOption(arg); } } else if ("-browser".equals(arg)) { // ok } else if (arg.startsWith("-tcp")) { if ("-tcp".equals(arg)) { // ok } else if ("-tcpAllowOthers".equals(arg)) { // no parameters } else if ("-tcpDaemon".equals(arg)) { // no parameters } else if ("-tcpSSL".equals(arg)) { // no parameters } else if ("-tcpPort".equals(arg)) { i++; } else if ("-tcpPassword".equals(arg)) { i++; } else if ("-tcpShutdown".equals(arg)) { i++; } else if ("-tcpShutdownForce".equals(arg)) { // ok } else { throwUnsupportedOption(arg); } } else if (arg.startsWith("-pg")) { if ("-pg".equals(arg)) { // ok } else if ("-pgAllowOthers".equals(arg)) { // no parameters } else if ("-pgDaemon".equals(arg)) { // no parameters } else if ("-pgPort".equals(arg)) { i++; } else { throwUnsupportedOption(arg); } } else if (arg.startsWith("-ftp")) { if ("-ftpPort".equals(arg)) { i++; } else if ("-ftpDir".equals(arg)) { i++; } else if ("-ftpRead".equals(arg)) { i++; } else if ("-ftpWrite".equals(arg)) { i++; } else if ("-ftpWritePassword".equals(arg)) { i++; } else if ("-ftpTask".equals(arg)) { // no parameters } else { throwUnsupportedOption(arg); } } else if ("-properties".equals(arg)) { i++; } else if ("-trace".equals(arg)) { // no parameters } else if ("-ifExists".equals(arg)) { // no parameters } else if ("-ifNotExists".equals(arg)) { // no parameters } else if ("-baseDir".equals(arg)) { i++; } else if ("-key".equals(arg)) { i += 2; } else if ("-tool".equals(arg)) { // no parameters } else { throwUnsupportedOption(arg); } } } @Override public void runTool(String... args) throws SQLException { boolean tcpStart = false, pgStart = false, webStart = false; boolean browserStart = false; boolean tcpShutdown = false, tcpShutdownForce = false; String tcpPassword = ""; String tcpShutdownServer = ""; boolean startDefaultServers = true; for (int i = 0; args != null && i < args.length; i++) { String arg = args[i]; if (arg == null) { } else if ("-?".equals(arg) || "-help".equals(arg)) { showUsage(); return; } else if (arg.startsWith("-web")) { if ("-web".equals(arg)) { startDefaultServers = false; webStart = true; } else if ("-webAllowOthers".equals(arg)) { // no parameters } else if ("-webDaemon".equals(arg)) { // no parameters } else if ("-webSSL".equals(arg)) { // no parameters } else if ("-webPort".equals(arg)) { i++; } else if ("-webAdminPassword".equals(arg)) { i += 2; } else { showUsageAndThrowUnsupportedOption(arg); } } else if ("-browser".equals(arg)) { startDefaultServers = false; browserStart = true; } else if (arg.startsWith("-tcp")) { if ("-tcp".equals(arg)) { startDefaultServers = false; tcpStart = true; } else if ("-tcpAllowOthers".equals(arg)) { // no parameters } else if ("-tcpDaemon".equals(arg)) { // no parameters } else if ("-tcpSSL".equals(arg)) { // no parameters } else if ("-tcpPort".equals(arg)) { i++; } else if ("-tcpPassword".equals(arg)) { tcpPassword = args[++i]; } else if ("-tcpShutdown".equals(arg)) { startDefaultServers = false; tcpShutdown = true; tcpShutdownServer = args[++i]; } else if ("-tcpShutdownForce".equals(arg)) { tcpShutdownForce = true; } else { showUsageAndThrowUnsupportedOption(arg); } } else if (arg.startsWith("-pg")) { if ("-pg".equals(arg)) { startDefaultServers = false; pgStart = true; } else if ("-pgAllowOthers".equals(arg)) { // no parameters } else if ("-pgDaemon".equals(arg)) { // no parameters } else if ("-pgPort".equals(arg)) { i++; } else { showUsageAndThrowUnsupportedOption(arg); } } else if ("-properties".equals(arg)) { i++; } else if ("-trace".equals(arg)) { // no parameters } else if ("-ifExists".equals(arg)) { // no parameters } else if ("-ifNotExists".equals(arg)) { // no parameters } else if ("-baseDir".equals(arg)) { i++; } else if ("-key".equals(arg)) { i += 2; } else { showUsageAndThrowUnsupportedOption(arg); } } verifyArgs(args); if (startDefaultServers) { tcpStart = true; pgStart = true; webStart = true; browserStart = true; } // TODO server: maybe use one single properties file? if (tcpShutdown) { out.println("Shutting down TCP Server at " + tcpShutdownServer); shutdownTcpServer(tcpShutdownServer, tcpPassword, tcpShutdownForce, false); } try { if (tcpStart) { tcp = createTcpServer(args); tcp.start(); out.println(tcp.getStatus()); tcp.setShutdownHandler(this); } if (pgStart) { pg = createPgServer(args); pg.start(); out.println(pg.getStatus()); } if (webStart) { web = createWebServer(args); web.setShutdownHandler(this); SQLException result = null; try { web.start(); } catch (Exception e) { result = DbException.toSQLException(e); } out.println(web.getStatus()); // start browser in any case (even if the server is already // running) because some people don't look at the output, but // are wondering why nothing happens if (browserStart) { try { openBrowser(web.getURL()); } catch (Exception e) { out.println(e.getMessage()); } } if (result != null) { throw result; } } else if (browserStart) { out.println("The browser can only start if a web server is started (-web)"); } } catch (SQLException e) { stopAll(); throw e; } }
Shutdown one or all TCP server. If force is set to false, the server will not allow new connections, but not kill existing connections, instead it will stop if the last connection is closed. If force is set to true, existing connections are killed. After calling the method with force=false, it is not possible to call it again with force=true because new connections are not allowed. Example:
Server.shutdownTcpServer("tcp://localhost:9094",
        password, true, false);
Params:
  • url – example: tcp://localhost:9094
  • password – the password to use ("" for no password)
  • force – the shutdown (don't wait)
  • all – whether all TCP servers that are running in the JVM should be stopped
/** * Shutdown one or all TCP server. If force is set to false, the server will * not allow new connections, but not kill existing connections, instead it * will stop if the last connection is closed. If force is set to true, * existing connections are killed. After calling the method with * force=false, it is not possible to call it again with force=true because * new connections are not allowed. Example: * * <pre> * Server.shutdownTcpServer(&quot;tcp://localhost:9094&quot;, * password, true, false); * </pre> * * @param url example: tcp://localhost:9094 * @param password the password to use ("" for no password) * @param force the shutdown (don't wait) * @param all whether all TCP servers that are running in the JVM should be * stopped */
public static void shutdownTcpServer(String url, String password, boolean force, boolean all) throws SQLException { TcpServer.shutdown(url, password, force, all); }
Get the status of this server.
Returns:the status
/** * Get the status of this server. * * @return the status */
public String getStatus() { StringBuilder buff = new StringBuilder(); if (!started) { buff.append("Not started"); } else if (isRunning(false)) { buff.append(service.getType()). append(" server running at "). append(service.getURL()). append(" ("); if (service.getAllowOthers()) { buff.append("others can connect"); } else { buff.append("only local connections"); } buff.append(')'); } else { buff.append("The "). append(service.getType()). append(" server could not be started. " + "Possible cause: another server is already running at "). append(service.getURL()); } return buff.toString(); }
Create a new web server, but does not start it yet. Example:
Server server = Server.createWebServer("-trace").start();
Supported options are: -webPort, -webSSL, -webAllowOthers, -webDaemon, -trace, -ifExists, -ifNotExists, -baseDir, -properties. See the main method for details.
Params:
  • args – the argument list
Returns:the server
/** * Create a new web server, but does not start it yet. Example: * * <pre> * Server server = Server.createWebServer("-trace").start(); * </pre> * Supported options are: * -webPort, -webSSL, -webAllowOthers, -webDaemon, * -trace, -ifExists, -ifNotExists, -baseDir, -properties. * See the main method for details. * * @param args the argument list * @return the server */
public static Server createWebServer(String... args) throws SQLException { return createWebServer(args, null, false); }
Create a new web server, but does not start it yet.
Params:
  • args – the argument list
  • key – key, or null
  • allowSecureCreation – whether creation of databases using the key should be allowed
Returns:the server
/** * Create a new web server, but does not start it yet. * * @param args * the argument list * @param key * key, or null * @param allowSecureCreation * whether creation of databases using the key should be allowed * @return the server */
static Server createWebServer(String[] args, String key, boolean allowSecureCreation) throws SQLException { WebServer service = new WebServer(); service.setKey(key); service.setAllowSecureCreation(allowSecureCreation); Server server = new Server(service, args); service.setShutdownHandler(server); return server; }
Create a new TCP server, but does not start it yet. Example:
Server server = Server.createTcpServer(
    "-tcpPort", "9123", "-tcpAllowOthers").start();
Supported options are: -tcpPort, -tcpSSL, -tcpPassword, -tcpAllowOthers, -tcpDaemon, -trace, -ifExists, -ifNotExists, -baseDir, -key. See the main method for details.

If no port is specified, the default port is used if possible, and if this port is already used, a random port is used. Use getPort() or getURL() after starting to retrieve the port.

Params:
  • args – the argument list
Returns:the server
/** * Create a new TCP server, but does not start it yet. Example: * * <pre> * Server server = Server.createTcpServer( * "-tcpPort", "9123", "-tcpAllowOthers").start(); * </pre> * Supported options are: * -tcpPort, -tcpSSL, -tcpPassword, -tcpAllowOthers, -tcpDaemon, * -trace, -ifExists, -ifNotExists, -baseDir, -key. * See the main method for details. * <p> * If no port is specified, the default port is used if possible, * and if this port is already used, a random port is used. * Use getPort() or getURL() after starting to retrieve the port. * </p> * * @param args the argument list * @return the server */
public static Server createTcpServer(String... args) throws SQLException { TcpServer service = new TcpServer(); Server server = new Server(service, args); service.setShutdownHandler(server); return server; }
Create a new PG server, but does not start it yet. Example:
Server server =
    Server.createPgServer("-pgAllowOthers").start();
Supported options are: -pgPort, -pgAllowOthers, -pgDaemon, -trace, -ifExists, -ifNotExists, -baseDir, -key. See the main method for details.

If no port is specified, the default port is used if possible, and if this port is already used, a random port is used. Use getPort() or getURL() after starting to retrieve the port.

Params:
  • args – the argument list
Returns:the server
/** * Create a new PG server, but does not start it yet. * Example: * <pre> * Server server = * Server.createPgServer("-pgAllowOthers").start(); * </pre> * Supported options are: * -pgPort, -pgAllowOthers, -pgDaemon, * -trace, -ifExists, -ifNotExists, -baseDir, -key. * See the main method for details. * <p> * If no port is specified, the default port is used if possible, * and if this port is already used, a random port is used. * Use getPort() or getURL() after starting to retrieve the port. * </p> * * @param args the argument list * @return the server */
public static Server createPgServer(String... args) throws SQLException { return new Server(new PgServer(), args); }
Tries to start the server.
Throws:
Returns:the server if successful
/** * Tries to start the server. * @return the server if successful * @throws SQLException if the server could not be started */
public Server start() throws SQLException { try { started = true; service.start(); String url = service.getURL(); int idx = url.indexOf('?'); if (idx >= 0) { url = url.substring(0, idx); } String name = service.getName() + " (" + url + ')'; Thread t = new Thread(this, name); t.setDaemon(service.isDaemon()); t.start(); for (int i = 1; i < 64; i += i) { wait(i); if (isRunning(false)) { return this; } } if (isRunning(true)) { return this; } throw DbException.get(ErrorCode.EXCEPTION_OPENING_PORT_2, name, "timeout; " + "please check your network configuration, specially the file /etc/hosts"); } catch (DbException e) { throw DbException.toSQLException(e); } } private static void wait(int i) { try { // sleep at most 4096 ms long sleep = (long) i * (long) i; Thread.sleep(sleep); } catch (InterruptedException e) { // ignore } } private void stopAll() { Server s = web; if (s != null && s.isRunning(false)) { s.stop(); web = null; } s = tcp; if (s != null && s.isRunning(false)) { s.stop(); tcp = null; } s = pg; if (s != null && s.isRunning(false)) { s.stop(); pg = null; } }
Checks if the server is running.
Params:
  • traceError – if errors should be written
Returns:if the server is running
/** * Checks if the server is running. * * @param traceError if errors should be written * @return if the server is running */
public boolean isRunning(boolean traceError) { return service.isRunning(traceError); }
Stops the server.
/** * Stops the server. */
public void stop() { started = false; if (service != null) { service.stop(); } }
Gets the URL of this server.
Returns:the url
/** * Gets the URL of this server. * * @return the url */
public String getURL() { return service.getURL(); }
Gets the port this server is listening on.
Returns:the port
/** * Gets the port this server is listening on. * * @return the port */
public int getPort() { return service.getPort(); }
INTERNAL
/** * INTERNAL */
@Override public void run() { try { service.listen(); } catch (Exception e) { DbException.traceThrowable(e); } }
INTERNAL
/** * INTERNAL */
public void setShutdownHandler(ShutdownHandler shutdownHandler) { this.shutdownHandler = shutdownHandler; }
INTERNAL
/** * INTERNAL */
@Override public void shutdown() { if (shutdownHandler != null) { shutdownHandler.shutdown(); } else { stopAll(); } }
Get the service attached to this server.
Returns:the service
/** * Get the service attached to this server. * * @return the service */
public Service getService() { return service; }
Open a new browser tab or window with the given URL.
Params:
  • url – the URL to open
/** * Open a new browser tab or window with the given URL. * * @param url the URL to open */
public static void openBrowser(String url) throws Exception { try { String osName = StringUtils.toLowerEnglish( Utils.getProperty("os.name", "linux")); Runtime rt = Runtime.getRuntime(); String browser = Utils.getProperty(SysProperties.H2_BROWSER, null); if (browser == null) { // under Linux, this will point to the default system browser try { browser = System.getenv("BROWSER"); } catch (SecurityException se) { // ignore } } if (browser != null) { if (browser.startsWith("call:")) { browser = browser.substring("call:".length()); Utils.callStaticMethod(browser, url); } else if (browser.contains("%url")) { String[] args = StringUtils.arraySplit(browser, ',', false); for (int i = 0; i < args.length; i++) { args[i] = StringUtils.replaceAll(args[i], "%url", url); } rt.exec(args); } else if (osName.contains("windows")) { rt.exec(new String[] { "cmd.exe", "/C", browser, url }); } else { rt.exec(new String[] { browser, url }); } return; } try { Class<?> desktopClass = Class.forName("java.awt.Desktop"); // Desktop.isDesktopSupported() Boolean supported = (Boolean) desktopClass. getMethod("isDesktopSupported"). invoke(null, new Object[0]); URI uri = new URI(url); if (supported) { // Desktop.getDesktop(); Object desktop = desktopClass.getMethod("getDesktop"). invoke(null); // desktop.browse(uri); desktopClass.getMethod("browse", URI.class). invoke(desktop, uri); return; } } catch (Exception e) { // ignore } if (osName.contains("windows")) { rt.exec(new String[] { "rundll32", "url.dll,FileProtocolHandler", url }); } else if (osName.contains("mac") || osName.contains("darwin")) { // Mac OS: to open a page with Safari, use "open -a Safari" Runtime.getRuntime().exec(new String[] { "open", url }); } else { String[] browsers = { "xdg-open", "chromium", "google-chrome", "firefox", "mozilla-firefox", "mozilla", "konqueror", "netscape", "opera", "midori" }; boolean ok = false; for (String b : browsers) { try { rt.exec(new String[] { b, url }); ok = true; break; } catch (Exception e) { // ignore and try the next } } if (!ok) { // No success in detection. throw new Exception( "Browser detection failed, and java property 'h2.browser' " + "and environment variable BROWSER are not set to a browser executable."); } } } catch (Exception e) { throw new Exception( "Failed to start a browser to open the URL " + url + ": " + e.getMessage()); } }
Start a web server and a browser that uses the given connection. The current transaction is preserved. This is specially useful to manually inspect the database when debugging. This method return as soon as the user has disconnected.
Params:
  • conn – the database connection (the database must be open)
/** * Start a web server and a browser that uses the given connection. The * current transaction is preserved. This is specially useful to manually * inspect the database when debugging. This method return as soon as the * user has disconnected. * * @param conn the database connection (the database must be open) */
public static void startWebServer(Connection conn) throws SQLException { startWebServer(conn, false); }
Start a web server and a browser that uses the given connection. The current transaction is preserved. This is specially useful to manually inspect the database when debugging. This method return as soon as the user has disconnected.
Params:
  • conn – the database connection (the database must be open)
  • ignoreProperties – if true properties from .h2.server.properties will be ignored
/** * Start a web server and a browser that uses the given connection. The * current transaction is preserved. This is specially useful to manually * inspect the database when debugging. This method return as soon as the * user has disconnected. * * @param conn the database connection (the database must be open) * @param ignoreProperties if {@code true} properties from * {@code .h2.server.properties} will be ignored */
public static void startWebServer(Connection conn, boolean ignoreProperties) throws SQLException { WebServer webServer = new WebServer(); String[] args; if (ignoreProperties) { args = new String[] { "-webPort", "0", "-properties", "null"}; } else { args = new String[] { "-webPort", "0" }; } Server web = new Server(webServer, args); web.start(); Server server = new Server(); server.web = web; webServer.setShutdownHandler(server); String url = webServer.addSession(conn); try { Server.openBrowser(url); while (!webServer.isStopped()) { Thread.sleep(1000); } } catch (Exception e) { // ignore } } }