/h2/src/main/org/h2/tools/Server.java
Java | 716 lines | 448 code | 28 blank | 240 comment | 191 complexity | 8dd1cd214d91a4729b484fe32f83c0ba MD5 | raw file
- /*
- * Copyright 2004-2014 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
- */
- 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.
- *
- * @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.
- * <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 <port>]</td>
- * <td>The port (default: 8082)</td></tr>
- * <tr><td>[-webSSL]</td>
- * <td>Use encrypted (HTTPS) connections</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 <port>]</td>
- * <td>The port (default: 9092)</td></tr>
- * <tr><td>[-tcpSSL]</td>
- * <td>Use encrypted (SSL) connections</td></tr>
- * <tr><td>[-tcpPassword <pwd>]</td>
- * <td>The password for shutting down a TCP server</td></tr>
- * <tr><td>[-tcpShutdown "<url>"]</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 <port>]</td>
- * <td>The port (default: 5435)</td></tr>
- * <tr><td>[-properties "<dir>"]</td>
- * <td>Server properties (default: ~, disable: null)</td></tr>
- * <tr><td>[-baseDir <dir>]</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>[-trace]</td>
- * <td>Print additional trace information (all servers)</td></tr>
- * <tr><td>[-key <from> <to>]</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) {
- continue;
- } 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 {
- 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 ("-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) {
- continue;
- } 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 {
- 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 ("-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:
- *
- * <pre>
- * Server.shutdownTcpServer("tcp://localhost:9094",
- * 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.
- *
- * @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:
- *
- * <pre>
- * Server server = Server.createWebServer("-trace").start();
- * </pre>
- * Supported options are:
- * -webPort, -webSSL, -webAllowOthers, -webDaemon,
- * -trace, -ifExists, -baseDir, -properties.
- * See the main method for details.
- *
- * @param args the argument list
- * @return the server
- */
- public static Server createWebServer(String... args) throws SQLException {
- WebServer service = new WebServer();
- Server server = new Server(service, args);
- service.setShutdownHandler(server);
- return 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, -baseDir, -key.
- * See the main method for details.
- *
- * @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:
- * <pre>
- * Server server =
- * Server.createPgServer("-pgAllowOthers").start();
- * </pre>
- * Supported options are:
- * -pgPort, -pgAllowOthers, -pgDaemon,
- * -trace, -ifExists, -baseDir, -key.
- * See the main method for details.
- *
- * @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.
- * @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 name = service.getName() + " (" + service.getURL() + ")";
- 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.
- *
- * @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.
- */
- public void stop() {
- started = false;
- if (service != null) {
- service.stop();
- }
- }
- /**
- * Gets the URL of this server.
- *
- * @return the url
- */
- public String getURL() {
- return service.getURL();
- }
- /**
- * Gets the port this server is listening on.
- *
- * @return the port
- */
- public int getPort() {
- return service.getPort();
- }
- /**
- * INTERNAL
- */
- @Override
- public void run() {
- try {
- service.listen();
- } catch (Exception e) {
- DbException.traceThrowable(e);
- }
- }
- /**
- * INTERNAL
- */
- public void setShutdownHandler(ShutdownHandler shutdownHandler) {
- this.shutdownHandler = shutdownHandler;
- }
- /**
- * INTERNAL
- */
- @Override
- public void shutdown() {
- if (shutdownHandler != null) {
- shutdownHandler.shutdown();
- } else {
- stopAll();
- }
- }
- /**
- * 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.
- *
- * @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, new Object[0]);
- // 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 = { "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 system property " +
- SysProperties.H2_BROWSER + " not set");
- }
- }
- } 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.
- *
- * @param conn the database connection (the database must be open)
- */
- public static void startWebServer(Connection conn) throws SQLException {
- WebServer webServer = new WebServer();
- Server web = new Server(webServer, new String[] { "-webPort", "0" });
- 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
- }
- }
- }