PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

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

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