PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/h2/src/main/org/h2/tools/Server.java

https://bitbucket.org/antiguru/h2database
Java | 716 lines | 448 code | 28 blank | 240 comment | 191 complexity | 8dd1cd214d91a4729b484fe32f83c0ba MD5 | raw file
  1. /*
  2. * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
  3. * and the EPL 1.0 (http://h2database.com/html/license.html).
  4. * Initial Developer: H2 Group
  5. */
  6. package org.h2.tools;
  7. import java.net.URI;
  8. import java.sql.Connection;
  9. import java.sql.SQLException;
  10. import org.h2.api.ErrorCode;
  11. import org.h2.engine.SysProperties;
  12. import org.h2.message.DbException;
  13. import org.h2.server.Service;
  14. import org.h2.server.ShutdownHandler;
  15. import org.h2.server.TcpServer;
  16. import org.h2.server.pg.PgServer;
  17. import org.h2.server.web.WebServer;
  18. import org.h2.util.StringUtils;
  19. import org.h2.util.Tool;
  20. import org.h2.util.Utils;
  21. /**
  22. * Starts the H2 Console (web-) server, TCP, and PG server.
  23. * @h2.resource
  24. */
  25. public class Server extends Tool implements Runnable, ShutdownHandler {
  26. private final Service service;
  27. private Server web, tcp, pg;
  28. private ShutdownHandler shutdownHandler;
  29. private boolean started;
  30. public Server() {
  31. // nothing to do
  32. this.service = null;
  33. }
  34. /**
  35. * Create a new server for the given service.
  36. *
  37. * @param service the service
  38. * @param args the command line arguments
  39. */
  40. public Server(Service service, String... args) throws SQLException {
  41. verifyArgs(args);
  42. this.service = service;
  43. try {
  44. service.init(args);
  45. } catch (Exception e) {
  46. throw DbException.toSQLException(e);
  47. }
  48. }
  49. /**
  50. * When running without options, -tcp, -web, -browser and -pg are started.
  51. * <br />
  52. * Options are case sensitive. Supported options are:
  53. * <table>
  54. * <tr><td>[-help] or [-?]</td>
  55. * <td>Print the list of options</td></tr>
  56. * <tr><td>[-web]</td>
  57. * <td>Start the web server with the H2 Console</td></tr>
  58. * <tr><td>[-webAllowOthers]</td>
  59. * <td>Allow other computers to connect - see below</td></tr>
  60. * <tr><td>[-webDaemon]</td>
  61. * <td>Use a daemon thread</td></tr>
  62. * <tr><td>[-webPort &lt;port&gt;]</td>
  63. * <td>The port (default: 8082)</td></tr>
  64. * <tr><td>[-webSSL]</td>
  65. * <td>Use encrypted (HTTPS) connections</td></tr>
  66. * <tr><td>[-browser]</td>
  67. * <td>Start a browser connecting to the web server</td></tr>
  68. * <tr><td>[-tcp]</td>
  69. * <td>Start the TCP server</td></tr>
  70. * <tr><td>[-tcpAllowOthers]</td>
  71. * <td>Allow other computers to connect - see below</td></tr>
  72. * <tr><td>[-tcpDaemon]</td>
  73. * <td>Use a daemon thread</td></tr>
  74. * <tr><td>[-tcpPort &lt;port&gt;]</td>
  75. * <td>The port (default: 9092)</td></tr>
  76. * <tr><td>[-tcpSSL]</td>
  77. * <td>Use encrypted (SSL) connections</td></tr>
  78. * <tr><td>[-tcpPassword &lt;pwd&gt;]</td>
  79. * <td>The password for shutting down a TCP server</td></tr>
  80. * <tr><td>[-tcpShutdown "&lt;url&gt;"]</td>
  81. * <td>Stop the TCP server; example: tcp://localhost</td></tr>
  82. * <tr><td>[-tcpShutdownForce]</td>
  83. * <td>Do not wait until all connections are closed</td></tr>
  84. * <tr><td>[-pg]</td>
  85. * <td>Start the PG server</td></tr>
  86. * <tr><td>[-pgAllowOthers]</td>
  87. * <td>Allow other computers to connect - see below</td></tr>
  88. * <tr><td>[-pgDaemon]</td>
  89. * <td>Use a daemon thread</td></tr>
  90. * <tr><td>[-pgPort &lt;port&gt;]</td>
  91. * <td>The port (default: 5435)</td></tr>
  92. * <tr><td>[-properties "&lt;dir&gt;"]</td>
  93. * <td>Server properties (default: ~, disable: null)</td></tr>
  94. * <tr><td>[-baseDir &lt;dir&gt;]</td>
  95. * <td>The base directory for H2 databases (all servers)</td></tr>
  96. * <tr><td>[-ifExists]</td>
  97. * <td>Only existing databases may be opened (all servers)</td></tr>
  98. * <tr><td>[-trace]</td>
  99. * <td>Print additional trace information (all servers)</td></tr>
  100. * <tr><td>[-key &lt;from&gt; &lt;to&gt;]</td>
  101. * <td>Allows to map a database name to another (all servers)</td></tr>
  102. * </table>
  103. * The options -xAllowOthers are potentially risky.
  104. * <br />
  105. * For details, see Advanced Topics / Protection against Remote Access.
  106. * @h2.resource
  107. *
  108. * @param args the command line arguments
  109. */
  110. public static void main(String... args) throws SQLException {
  111. new Server().runTool(args);
  112. }
  113. private void verifyArgs(String... args) throws SQLException {
  114. for (int i = 0; args != null && i < args.length; i++) {
  115. String arg = args[i];
  116. if (arg == null) {
  117. continue;
  118. } else if ("-?".equals(arg) || "-help".equals(arg)) {
  119. // ok
  120. } else if (arg.startsWith("-web")) {
  121. if ("-web".equals(arg)) {
  122. // ok
  123. } else if ("-webAllowOthers".equals(arg)) {
  124. // no parameters
  125. } else if ("-webDaemon".equals(arg)) {
  126. // no parameters
  127. } else if ("-webSSL".equals(arg)) {
  128. // no parameters
  129. } else if ("-webPort".equals(arg)) {
  130. i++;
  131. } else {
  132. throwUnsupportedOption(arg);
  133. }
  134. } else if ("-browser".equals(arg)) {
  135. // ok
  136. } else if (arg.startsWith("-tcp")) {
  137. if ("-tcp".equals(arg)) {
  138. // ok
  139. } else if ("-tcpAllowOthers".equals(arg)) {
  140. // no parameters
  141. } else if ("-tcpDaemon".equals(arg)) {
  142. // no parameters
  143. } else if ("-tcpSSL".equals(arg)) {
  144. // no parameters
  145. } else if ("-tcpPort".equals(arg)) {
  146. i++;
  147. } else if ("-tcpPassword".equals(arg)) {
  148. i++;
  149. } else if ("-tcpShutdown".equals(arg)) {
  150. i++;
  151. } else if ("-tcpShutdownForce".equals(arg)) {
  152. // ok
  153. } else {
  154. throwUnsupportedOption(arg);
  155. }
  156. } else if (arg.startsWith("-pg")) {
  157. if ("-pg".equals(arg)) {
  158. // ok
  159. } else if ("-pgAllowOthers".equals(arg)) {
  160. // no parameters
  161. } else if ("-pgDaemon".equals(arg)) {
  162. // no parameters
  163. } else if ("-pgPort".equals(arg)) {
  164. i++;
  165. } else {
  166. throwUnsupportedOption(arg);
  167. }
  168. } else if (arg.startsWith("-ftp")) {
  169. if ("-ftpPort".equals(arg)) {
  170. i++;
  171. } else if ("-ftpDir".equals(arg)) {
  172. i++;
  173. } else if ("-ftpRead".equals(arg)) {
  174. i++;
  175. } else if ("-ftpWrite".equals(arg)) {
  176. i++;
  177. } else if ("-ftpWritePassword".equals(arg)) {
  178. i++;
  179. } else if ("-ftpTask".equals(arg)) {
  180. // no parameters
  181. } else {
  182. throwUnsupportedOption(arg);
  183. }
  184. } else if ("-properties".equals(arg)) {
  185. i++;
  186. } else if ("-trace".equals(arg)) {
  187. // no parameters
  188. } else if ("-ifExists".equals(arg)) {
  189. // no parameters
  190. } else if ("-baseDir".equals(arg)) {
  191. i++;
  192. } else if ("-key".equals(arg)) {
  193. i += 2;
  194. } else if ("-tool".equals(arg)) {
  195. // no parameters
  196. } else {
  197. throwUnsupportedOption(arg);
  198. }
  199. }
  200. }
  201. @Override
  202. public void runTool(String... args) throws SQLException {
  203. boolean tcpStart = false, pgStart = false, webStart = false;
  204. boolean browserStart = false;
  205. boolean tcpShutdown = false, tcpShutdownForce = false;
  206. String tcpPassword = "";
  207. String tcpShutdownServer = "";
  208. boolean startDefaultServers = true;
  209. for (int i = 0; args != null && i < args.length; i++) {
  210. String arg = args[i];
  211. if (arg == null) {
  212. continue;
  213. } else if ("-?".equals(arg) || "-help".equals(arg)) {
  214. showUsage();
  215. return;
  216. } else if (arg.startsWith("-web")) {
  217. if ("-web".equals(arg)) {
  218. startDefaultServers = false;
  219. webStart = true;
  220. } else if ("-webAllowOthers".equals(arg)) {
  221. // no parameters
  222. } else if ("-webDaemon".equals(arg)) {
  223. // no parameters
  224. } else if ("-webSSL".equals(arg)) {
  225. // no parameters
  226. } else if ("-webPort".equals(arg)) {
  227. i++;
  228. } else {
  229. showUsageAndThrowUnsupportedOption(arg);
  230. }
  231. } else if ("-browser".equals(arg)) {
  232. startDefaultServers = false;
  233. browserStart = true;
  234. } else if (arg.startsWith("-tcp")) {
  235. if ("-tcp".equals(arg)) {
  236. startDefaultServers = false;
  237. tcpStart = true;
  238. } else if ("-tcpAllowOthers".equals(arg)) {
  239. // no parameters
  240. } else if ("-tcpDaemon".equals(arg)) {
  241. // no parameters
  242. } else if ("-tcpSSL".equals(arg)) {
  243. // no parameters
  244. } else if ("-tcpPort".equals(arg)) {
  245. i++;
  246. } else if ("-tcpPassword".equals(arg)) {
  247. tcpPassword = args[++i];
  248. } else if ("-tcpShutdown".equals(arg)) {
  249. startDefaultServers = false;
  250. tcpShutdown = true;
  251. tcpShutdownServer = args[++i];
  252. } else if ("-tcpShutdownForce".equals(arg)) {
  253. tcpShutdownForce = true;
  254. } else {
  255. showUsageAndThrowUnsupportedOption(arg);
  256. }
  257. } else if (arg.startsWith("-pg")) {
  258. if ("-pg".equals(arg)) {
  259. startDefaultServers = false;
  260. pgStart = true;
  261. } else if ("-pgAllowOthers".equals(arg)) {
  262. // no parameters
  263. } else if ("-pgDaemon".equals(arg)) {
  264. // no parameters
  265. } else if ("-pgPort".equals(arg)) {
  266. i++;
  267. } else {
  268. showUsageAndThrowUnsupportedOption(arg);
  269. }
  270. } else if ("-properties".equals(arg)) {
  271. i++;
  272. } else if ("-trace".equals(arg)) {
  273. // no parameters
  274. } else if ("-ifExists".equals(arg)) {
  275. // no parameters
  276. } else if ("-baseDir".equals(arg)) {
  277. i++;
  278. } else if ("-key".equals(arg)) {
  279. i += 2;
  280. } else {
  281. showUsageAndThrowUnsupportedOption(arg);
  282. }
  283. }
  284. verifyArgs(args);
  285. if (startDefaultServers) {
  286. tcpStart = true;
  287. pgStart = true;
  288. webStart = true;
  289. browserStart = true;
  290. }
  291. // TODO server: maybe use one single properties file?
  292. if (tcpShutdown) {
  293. out.println("Shutting down TCP Server at " + tcpShutdownServer);
  294. shutdownTcpServer(tcpShutdownServer, tcpPassword,
  295. tcpShutdownForce, false);
  296. }
  297. try {
  298. if (tcpStart) {
  299. tcp = createTcpServer(args);
  300. tcp.start();
  301. out.println(tcp.getStatus());
  302. tcp.setShutdownHandler(this);
  303. }
  304. if (pgStart) {
  305. pg = createPgServer(args);
  306. pg.start();
  307. out.println(pg.getStatus());
  308. }
  309. if (webStart) {
  310. web = createWebServer(args);
  311. web.setShutdownHandler(this);
  312. SQLException result = null;
  313. try {
  314. web.start();
  315. } catch (Exception e) {
  316. result = DbException.toSQLException(e);
  317. }
  318. out.println(web.getStatus());
  319. // start browser in any case (even if the server is already
  320. // running) because some people don't look at the output, but
  321. // are wondering why nothing happens
  322. if (browserStart) {
  323. try {
  324. openBrowser(web.getURL());
  325. } catch (Exception e) {
  326. out.println(e.getMessage());
  327. }
  328. }
  329. if (result != null) {
  330. throw result;
  331. }
  332. } else if (browserStart) {
  333. out.println("The browser can only start if a web server is started (-web)");
  334. }
  335. } catch (SQLException e) {
  336. stopAll();
  337. throw e;
  338. }
  339. }
  340. /**
  341. * Shutdown one or all TCP server. If force is set to false, the server will
  342. * not allow new connections, but not kill existing connections, instead it
  343. * will stop if the last connection is closed. If force is set to true,
  344. * existing connections are killed. After calling the method with
  345. * force=false, it is not possible to call it again with force=true because
  346. * new connections are not allowed. Example:
  347. *
  348. * <pre>
  349. * Server.shutdownTcpServer(&quot;tcp://localhost:9094&quot;,
  350. * password, true, false);
  351. * </pre>
  352. *
  353. * @param url example: tcp://localhost:9094
  354. * @param password the password to use ("" for no password)
  355. * @param force the shutdown (don't wait)
  356. * @param all whether all TCP servers that are running in the JVM should be
  357. * stopped
  358. */
  359. public static void shutdownTcpServer(String url, String password,
  360. boolean force, boolean all) throws SQLException {
  361. TcpServer.shutdown(url, password, force, all);
  362. }
  363. /**
  364. * Get the status of this server.
  365. *
  366. * @return the status
  367. */
  368. public String getStatus() {
  369. StringBuilder buff = new StringBuilder();
  370. if (!started) {
  371. buff.append("Not started");
  372. } else if (isRunning(false)) {
  373. buff.append(service.getType()).
  374. append(" server running at ").
  375. append(service.getURL()).
  376. append(" (");
  377. if (service.getAllowOthers()) {
  378. buff.append("others can connect");
  379. } else {
  380. buff.append("only local connections");
  381. }
  382. buff.append(')');
  383. } else {
  384. buff.append("The ").
  385. append(service.getType()).
  386. append(" server could not be started. " +
  387. "Possible cause: another server is already running at ").
  388. append(service.getURL());
  389. }
  390. return buff.toString();
  391. }
  392. /**
  393. * Create a new web server, but does not start it yet. Example:
  394. *
  395. * <pre>
  396. * Server server = Server.createWebServer("-trace").start();
  397. * </pre>
  398. * Supported options are:
  399. * -webPort, -webSSL, -webAllowOthers, -webDaemon,
  400. * -trace, -ifExists, -baseDir, -properties.
  401. * See the main method for details.
  402. *
  403. * @param args the argument list
  404. * @return the server
  405. */
  406. public static Server createWebServer(String... args) throws SQLException {
  407. WebServer service = new WebServer();
  408. Server server = new Server(service, args);
  409. service.setShutdownHandler(server);
  410. return server;
  411. }
  412. /**
  413. * Create a new TCP server, but does not start it yet. Example:
  414. *
  415. * <pre>
  416. * Server server = Server.createTcpServer(
  417. * "-tcpPort", "9123", "-tcpAllowOthers").start();
  418. * </pre>
  419. * Supported options are:
  420. * -tcpPort, -tcpSSL, -tcpPassword, -tcpAllowOthers, -tcpDaemon,
  421. * -trace, -ifExists, -baseDir, -key.
  422. * See the main method for details.
  423. *
  424. * @param args the argument list
  425. * @return the server
  426. */
  427. public static Server createTcpServer(String... args) throws SQLException {
  428. TcpServer service = new TcpServer();
  429. Server server = new Server(service, args);
  430. service.setShutdownHandler(server);
  431. return server;
  432. }
  433. /**
  434. * Create a new PG server, but does not start it yet.
  435. * Example:
  436. * <pre>
  437. * Server server =
  438. * Server.createPgServer("-pgAllowOthers").start();
  439. * </pre>
  440. * Supported options are:
  441. * -pgPort, -pgAllowOthers, -pgDaemon,
  442. * -trace, -ifExists, -baseDir, -key.
  443. * See the main method for details.
  444. *
  445. * @param args the argument list
  446. * @return the server
  447. */
  448. public static Server createPgServer(String... args) throws SQLException {
  449. return new Server(new PgServer(), args);
  450. }
  451. /**
  452. * Tries to start the server.
  453. * @return the server if successful
  454. * @throws SQLException if the server could not be started
  455. */
  456. public Server start() throws SQLException {
  457. try {
  458. started = true;
  459. service.start();
  460. String name = service.getName() + " (" + service.getURL() + ")";
  461. Thread t = new Thread(this, name);
  462. t.setDaemon(service.isDaemon());
  463. t.start();
  464. for (int i = 1; i < 64; i += i) {
  465. wait(i);
  466. if (isRunning(false)) {
  467. return this;
  468. }
  469. }
  470. if (isRunning(true)) {
  471. return this;
  472. }
  473. throw DbException.get(ErrorCode.EXCEPTION_OPENING_PORT_2,
  474. name, "timeout; " +
  475. "please check your network configuration, specially the file /etc/hosts");
  476. } catch (DbException e) {
  477. throw DbException.toSQLException(e);
  478. }
  479. }
  480. private static void wait(int i) {
  481. try {
  482. // sleep at most 4096 ms
  483. long sleep = (long) i * (long) i;
  484. Thread.sleep(sleep);
  485. } catch (InterruptedException e) {
  486. // ignore
  487. }
  488. }
  489. private void stopAll() {
  490. Server s = web;
  491. if (s != null && s.isRunning(false)) {
  492. s.stop();
  493. web = null;
  494. }
  495. s = tcp;
  496. if (s != null && s.isRunning(false)) {
  497. s.stop();
  498. tcp = null;
  499. }
  500. s = pg;
  501. if (s != null && s.isRunning(false)) {
  502. s.stop();
  503. pg = null;
  504. }
  505. }
  506. /**
  507. * Checks if the server is running.
  508. *
  509. * @param traceError if errors should be written
  510. * @return if the server is running
  511. */
  512. public boolean isRunning(boolean traceError) {
  513. return service.isRunning(traceError);
  514. }
  515. /**
  516. * Stops the server.
  517. */
  518. public void stop() {
  519. started = false;
  520. if (service != null) {
  521. service.stop();
  522. }
  523. }
  524. /**
  525. * Gets the URL of this server.
  526. *
  527. * @return the url
  528. */
  529. public String getURL() {
  530. return service.getURL();
  531. }
  532. /**
  533. * Gets the port this server is listening on.
  534. *
  535. * @return the port
  536. */
  537. public int getPort() {
  538. return service.getPort();
  539. }
  540. /**
  541. * INTERNAL
  542. */
  543. @Override
  544. public void run() {
  545. try {
  546. service.listen();
  547. } catch (Exception e) {
  548. DbException.traceThrowable(e);
  549. }
  550. }
  551. /**
  552. * INTERNAL
  553. */
  554. public void setShutdownHandler(ShutdownHandler shutdownHandler) {
  555. this.shutdownHandler = shutdownHandler;
  556. }
  557. /**
  558. * INTERNAL
  559. */
  560. @Override
  561. public void shutdown() {
  562. if (shutdownHandler != null) {
  563. shutdownHandler.shutdown();
  564. } else {
  565. stopAll();
  566. }
  567. }
  568. /**
  569. * Get the service attached to this server.
  570. *
  571. * @return the service
  572. */
  573. public Service getService() {
  574. return service;
  575. }
  576. /**
  577. * Open a new browser tab or window with the given URL.
  578. *
  579. * @param url the URL to open
  580. */
  581. public static void openBrowser(String url) throws Exception {
  582. try {
  583. String osName = StringUtils.toLowerEnglish(
  584. Utils.getProperty("os.name", "linux"));
  585. Runtime rt = Runtime.getRuntime();
  586. String browser = Utils.getProperty(SysProperties.H2_BROWSER, null);
  587. if (browser == null) {
  588. // under Linux, this will point to the default system browser
  589. try {
  590. browser = System.getenv("BROWSER");
  591. } catch (SecurityException se) {
  592. // ignore
  593. }
  594. }
  595. if (browser != null) {
  596. if (browser.startsWith("call:")) {
  597. browser = browser.substring("call:".length());
  598. Utils.callStaticMethod(browser, url);
  599. } else if (browser.contains("%url")) {
  600. String[] args = StringUtils.arraySplit(browser, ',', false);
  601. for (int i = 0; i < args.length; i++) {
  602. args[i] = StringUtils.replaceAll(args[i], "%url", url);
  603. }
  604. rt.exec(args);
  605. } else if (osName.contains("windows")) {
  606. rt.exec(new String[] { "cmd.exe", "/C", browser, url });
  607. } else {
  608. rt.exec(new String[] { browser, url });
  609. }
  610. return;
  611. }
  612. try {
  613. Class<?> desktopClass = Class.forName("java.awt.Desktop");
  614. // Desktop.isDesktopSupported()
  615. Boolean supported = (Boolean) desktopClass.
  616. getMethod("isDesktopSupported").
  617. invoke(null, new Object[0]);
  618. URI uri = new URI(url);
  619. if (supported) {
  620. // Desktop.getDesktop();
  621. Object desktop = desktopClass.getMethod("getDesktop").
  622. invoke(null, new Object[0]);
  623. // desktop.browse(uri);
  624. desktopClass.getMethod("browse", URI.class).
  625. invoke(desktop, uri);
  626. return;
  627. }
  628. } catch (Exception e) {
  629. // ignore
  630. }
  631. if (osName.contains("windows")) {
  632. rt.exec(new String[] { "rundll32", "url.dll,FileProtocolHandler", url });
  633. } else if (osName.contains("mac") || osName.contains("darwin")) {
  634. // Mac OS: to open a page with Safari, use "open -a Safari"
  635. Runtime.getRuntime().exec(new String[] { "open", url });
  636. } else {
  637. String[] browsers = { "chromium", "google-chrome", "firefox",
  638. "mozilla-firefox", "mozilla", "konqueror", "netscape",
  639. "opera", "midori" };
  640. boolean ok = false;
  641. for (String b : browsers) {
  642. try {
  643. rt.exec(new String[] { b, url });
  644. ok = true;
  645. break;
  646. } catch (Exception e) {
  647. // ignore and try the next
  648. }
  649. }
  650. if (!ok) {
  651. // No success in detection.
  652. throw new Exception(
  653. "Browser detection failed and system property " +
  654. SysProperties.H2_BROWSER + " not set");
  655. }
  656. }
  657. } catch (Exception e) {
  658. throw new Exception(
  659. "Failed to start a browser to open the URL " +
  660. url + ": " + e.getMessage());
  661. }
  662. }
  663. /**
  664. * Start a web server and a browser that uses the given connection. The
  665. * current transaction is preserved. This is specially useful to manually
  666. * inspect the database when debugging. This method return as soon as the
  667. * user has disconnected.
  668. *
  669. * @param conn the database connection (the database must be open)
  670. */
  671. public static void startWebServer(Connection conn) throws SQLException {
  672. WebServer webServer = new WebServer();
  673. Server web = new Server(webServer, new String[] { "-webPort", "0" });
  674. web.start();
  675. Server server = new Server();
  676. server.web = web;
  677. webServer.setShutdownHandler(server);
  678. String url = webServer.addSession(conn);
  679. try {
  680. Server.openBrowser(url);
  681. while (!webServer.isStopped()) {
  682. Thread.sleep(1000);
  683. }
  684. } catch (Exception e) {
  685. // ignore
  686. }
  687. }
  688. }