001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.tools;
007:
008: import java.io.PrintStream;
009: import java.sql.SQLException;
010:
011: import org.h2.constant.ErrorCode;
012: import org.h2.engine.Constants;
013: import org.h2.message.Message;
014: import org.h2.message.TraceSystem;
015: import org.h2.server.Service;
016: import org.h2.server.ShutdownHandler;
017: import org.h2.server.TcpServer;
018: import org.h2.server.ftp.FtpServer;
019: import org.h2.server.pg.PgServer;
020: import org.h2.server.web.WebServer;
021: import org.h2.util.StartBrowser;
022:
023: /**
024: * This tool can be used to start various database servers (listeners).
025: */
026: public class Server implements Runnable, ShutdownHandler {
027:
028: private Service service;
029: private static final int EXIT_ERROR = 1;
030: private Server web, tcp, pg, ftp;
031: private ShutdownHandler shutdownHandler;
032:
033: private void showUsage(String a, PrintStream out) {
034: if (a != null) {
035: out.println("Unknown option: " + a);
036: out.println();
037: }
038: out.println("java " + getClass().getName() + " [options]");
039: out
040: .println("See also http://h2database.com/javadoc/org/h2/tools/Server.html");
041: out
042: .println("By default, -tcp, -web, -browser and -pg are started.");
043: out.println("Options are case sensitive. Options:");
044: out.println();
045: out.println("-web (start the Web Server and H2 Console)");
046: out.println("-webAllowOthers [true|false}");
047: out.println("-webPort <port> (default: "
048: + Constants.DEFAULT_HTTP_PORT + ")");
049: out.println("-webSSL [true|false}");
050: out.println();
051: out
052: .println("-browser (start a browser to connect to the H2 Console)");
053: out.println();
054: out.println("-tcp (start the TCP Server)");
055: out.println("-tcpAllowOthers {true|false}");
056: out.println("-tcpPort <port> (default: "
057: + TcpServer.DEFAULT_PORT + ")");
058: out.println("-tcpSSL {true|false}");
059: out
060: .println("-tcpPassword {password} (the password for shutting down a TCP Server)");
061: out
062: .println("-tcpShutdown {url} (shutdown the TCP Server, URL example: tcp://localhost:9094)");
063: out
064: .println("-tcpShutdownForce {true|false} (don't wait for other connections to close)");
065: out.println();
066: out.println("-pg (start the PG Server)");
067: out.println("-pgAllowOthers {true|false}");
068: out.println("-pgPort <port> (default: " + PgServer.DEFAULT_PORT
069: + ")");
070: out.println();
071: out.println("-ftp (start the FTP Server)");
072: out.println("-ftpPort <port> (default: "
073: + Constants.DEFAULT_FTP_PORT + ")");
074: out.println("-ftpDir <directory> (default: "
075: + FtpServer.DEFAULT_ROOT
076: + ", use jdbc:... to access a database)");
077: out.println("-ftpRead <readUserName> (default: "
078: + FtpServer.DEFAULT_READ + ")");
079: out.println("-ftpWrite <writeUserName> (default: "
080: + FtpServer.DEFAULT_WRITE + ")");
081: out.println("-ftpWritePassword <password> (default: "
082: + FtpServer.DEFAULT_WRITE_PASSWORD + ")");
083: out.println();
084: out
085: .println("-log {true|false} (enable or disable logging, for all servers)");
086: out
087: .println("-baseDir <directory> (sets the base directory for H2 databases, for all servers)");
088: out
089: .println("-ifExists {true|false} (only existing databases may be opened, for all servers)");
090: }
091:
092: public Server() {
093: }
094:
095: /**
096: * The command line interface for this tool. The options must be split into
097: * strings like this: "-baseDir", "/temp/data",... By default, -tcp, -web,
098: * -browser and -pg are started. If there is a problem starting a service,
099: * the program terminates with an exit code of 1. Options are case
100: * sensitive. The following options are supported:
101: * <ul>
102: * <li>-help or -? (print the list of options) </li>
103: * <li>-web (start the Web Server and H2 Console) </li>
104: * <li>-tcp (start the TCP Server) </li>
105: * <li>-tcpShutdown {url} (shutdown the running TCP Server,
106: * URL example: tcp://localhost:9094) </li>
107: * <li>-pg (start the PG Server) </li>
108: * <li>-browser (start a browser and open a page to connect to the
109: * Web Server) </li>
110: * <li>-log {true|false} (enable or disable logging, for all servers) </li>
111: * <li>-baseDir {directory} (sets the base directory for H2 databases,
112: * for all servers) </li>
113: * <li>-ifExists {true|false} (only existing databases may be opened,
114: * for all servers) </li>
115: * <li>-ftp (start the FTP Server) </li>
116: * </ul>
117: * For each Server, additional options are available:
118: * <ul>
119: * <li>-webPort {port} (the port of Web Server, default: 8082) </li>
120: * <li>-webSSL {true|false} (if SSL should be used) </li>
121: * <li>-webAllowOthers {true|false} (enable/disable remote connections)
122: * </li>
123: * <li>-tcpPort {port} (the port of TCP Server, default: 9092) </li>
124: * <li>-tcpSSL {true|false} (if SSL should be used) </li>
125: * <li>-tcpAllowOthers {true|false} (enable/disable remote connections)
126: * </li>
127: * <li>-tcpPassword {password} (the password for shutting down a TCP
128: * Server) </li>
129: * <li>-tcpShutdownForce {true|false} (don't wait for other connections to
130: * close) </li>
131: * <li>-pgPort {port} (the port of PG Server, default: 5435) </li>
132: * <li>-pgAllowOthers {true|false} (enable/disable remote connections)
133: * </li>
134: * <li>-ftpPort {port} </li>
135: * <li>-ftpDir {directory} </li>
136: * <li>-ftpRead {readUserName} </li>
137: * <li>-ftpWrite {writeUserName} </li>
138: * <li>-ftpWritePassword {password} </li>
139: * </ul>
140: *
141: * @param args the command line arguments
142: * @throws SQLException
143: */
144: public static void main(String[] args) throws SQLException {
145: int exitCode = new Server().run(args, System.out);
146: if (exitCode != 0) {
147: System.exit(exitCode);
148: }
149: }
150:
151: /**
152: * INTERNAL
153: */
154: public int run(String[] args, PrintStream out) throws SQLException {
155: boolean tcpStart = false, pgStart = false, webStart = false, ftpStart = false;
156: boolean browserStart = false;
157: boolean tcpShutdown = false, tcpShutdownForce = false;
158: String tcpPassword = "";
159: String tcpShutdownServer = "";
160: boolean startDefaultServers = true;
161: for (int i = 0; args != null && i < args.length; i++) {
162: String a = args[i];
163: if (a == null) {
164: continue;
165: } else if ("-?".equals(a) || "-help".equals(a)) {
166: showUsage(null, out);
167: return EXIT_ERROR;
168: } else if (a.startsWith("-web")) {
169: if ("-web".equals(a)) {
170: startDefaultServers = false;
171: webStart = true;
172: } else if ("-webAllowOthers".equals(a)) {
173: i++;
174: } else if ("-webPort".equals(a)) {
175: i++;
176: } else if ("-webScript".equals(a)) {
177: i++;
178: } else if ("-webSSL".equals(a)) {
179: i++;
180: } else {
181: showUsage(a, out);
182: return EXIT_ERROR;
183: }
184: } else if ("-browser".equals(a)) {
185: startDefaultServers = false;
186: browserStart = true;
187: } else if (a.startsWith("-tcp")) {
188: if ("-tcp".equals(a)) {
189: startDefaultServers = false;
190: tcpStart = true;
191: } else if ("-tcpAllowOthers".equals(a)) {
192: i++;
193: } else if ("-tcpPort".equals(a)) {
194: i++;
195: } else if ("-tcpSSL".equals(a)) {
196: i++;
197: } else if ("-tcpPassword".equals(a)) {
198: tcpPassword = args[++i];
199: } else if ("-tcpShutdown".equals(a)) {
200: startDefaultServers = false;
201: tcpShutdown = true;
202: tcpShutdownServer = args[++i];
203: } else if ("-tcpShutdownForce".equals(a)) {
204: tcpShutdownForce = Boolean.valueOf(args[++i])
205: .booleanValue();
206: } else {
207: showUsage(a, out);
208: return EXIT_ERROR;
209: }
210: } else if (a.startsWith("-pg")) {
211: if ("-pg".equals(a)) {
212: startDefaultServers = false;
213: pgStart = true;
214: } else if ("-pgAllowOthers".equals(a)) {
215: i++;
216: } else if ("-pgPort".equals(a)) {
217: i++;
218: } else {
219: showUsage(a, out);
220: return EXIT_ERROR;
221: }
222: } else if (a.startsWith("-ftp")) {
223: if ("-ftp".equals(a)) {
224: startDefaultServers = false;
225: ftpStart = true;
226: } else if ("-ftpPort".equals(a)) {
227: i++;
228: } else if ("-ftpDir".equals(a)) {
229: i++;
230: } else if ("-ftpRead".equals(a)) {
231: i++;
232: } else if ("-ftpWrite".equals(a)) {
233: i++;
234: } else if ("-ftpWritePassword".equals(a)) {
235: i++;
236: } else if ("-ftpTask".equals(a)) {
237: i++;
238: } else {
239: showUsage(a, out);
240: return EXIT_ERROR;
241: }
242: } else if (a.startsWith("-log")) {
243: i++;
244: } else if ("-baseDir".equals(a)) {
245: i++;
246: } else if ("-ifExists".equals(a)) {
247: i++;
248: } else {
249: showUsage(a, out);
250: return EXIT_ERROR;
251: }
252: }
253: int exitCode = 0;
254: if (startDefaultServers) {
255: tcpStart = true;
256: pgStart = true;
257: webStart = true;
258: browserStart = true;
259: }
260: // TODO server: maybe use one single properties file?
261: if (tcpShutdown) {
262: out.println("Shutting down TCP Server at "
263: + tcpShutdownServer);
264: shutdownTcpServer(tcpShutdownServer, tcpPassword,
265: tcpShutdownForce);
266: }
267: if (tcpStart) {
268: tcp = createTcpServer(args);
269: try {
270: tcp.start();
271: } catch (SQLException e) {
272: // ignore (status is displayed)
273: e.printStackTrace();
274: exitCode = EXIT_ERROR;
275: }
276: out.println(tcp.getStatus());
277: }
278: if (pgStart) {
279: pg = createPgServer(args);
280: try {
281: pg.start();
282: } catch (SQLException e) {
283: // ignore (status is displayed)
284: e.printStackTrace();
285: exitCode = EXIT_ERROR;
286: }
287: out.println(pg.getStatus());
288: }
289: if (webStart) {
290: web = createWebServer(args);
291: web.setShutdownHandler(this );
292: try {
293: web.start();
294: } catch (SQLException e) {
295: // ignore (status is displayed)
296: e.printStackTrace();
297: exitCode = EXIT_ERROR;
298: }
299: out.println(web.getStatus());
300: // start browser anyway (even if the server is already running)
301: // because some people don't look at the output,
302: // but are wondering why nothing happens
303: if (browserStart) {
304: StartBrowser.openURL(web.getURL());
305: }
306: }
307: if (ftpStart) {
308: ftp = createFtpServer(args);
309: try {
310: ftp.start();
311: } catch (SQLException e) {
312: // ignore (status is displayed)
313: e.printStackTrace();
314: exitCode = EXIT_ERROR;
315: }
316: out.println(ftp.getStatus());
317: }
318: return exitCode;
319: }
320:
321: /**
322: * Shutdown a TCP server. If force is set to false, the server will not
323: * allow new connections, but not kill existing connections, instead it will
324: * stop if the last connection is closed. If force is set to true, existing
325: * connections are killed. After calling the method with force=false, it is
326: * not possible to call it again with force=true because new connections are
327: * not allowed. Example:
328: *
329: * <pre>
330: * Server.shutdownTcpServer("tcp://localhost:9094", password, true);
331: * </pre>
332: *
333: * @param url example: tcp://localhost:9094
334: * @param password the password to use ("" for no password)
335: * @param force the shutdown (don't wait)
336: * @throws ClassNotFoundException
337: * @throws SQLException
338: */
339: public static void shutdownTcpServer(String url, String password,
340: boolean force) throws SQLException {
341: TcpServer.shutdown(url, password, force);
342: }
343:
344: String getStatus() {
345: StringBuffer buff = new StringBuffer();
346: if (isRunning()) {
347: buff.append(service.getType());
348: buff.append(" server running on ");
349: buff.append(service.getURL());
350: buff.append(" (");
351: if (service.getAllowOthers()) {
352: buff.append("others can connect");
353: } else {
354: buff.append("only local connections");
355: }
356: buff.append(")");
357: } else {
358: buff
359: .append("Port is in use, maybe another "
360: + service.getType()
361: + " server already running on ");
362: buff.append(service.getURL());
363: }
364: return buff.toString();
365: }
366:
367: /**
368: * Create a new web server, but does not start it yet. Example:
369: *
370: * <pre>
371: * Server server = Server.createWebServer(
372: * new String[] { "-log", "true" }).start();
373: * </pre>
374: *
375: * @param args
376: * @return the server
377: */
378: public static Server createWebServer(String[] args)
379: throws SQLException {
380: WebServer service = new WebServer();
381: Server server = new Server(service, args);
382: service.setShutdownHandler(server);
383: return server;
384: }
385:
386: /**
387: * Create a new ftp server, but does not start it yet. Example:
388: *
389: * <pre>
390: * Server server = Server.createFtpServer(
391: * new String[] { "-log", "true" }).start();
392: * </pre>
393: *
394: * @param args
395: * @return the server
396: */
397: public static Server createFtpServer(String[] args)
398: throws SQLException {
399: return new Server(new FtpServer(), args);
400: }
401:
402: /**
403: * Create a new TCP server, but does not start it yet. Example:
404: *
405: * <pre>
406: * Server server = Server.createTcpServer(
407: * new String[] { "-tcpAllowOthers", "true" }).start();
408: * </pre>
409: *
410: * @param args
411: * @return the server
412: */
413: public static Server createTcpServer(String[] args)
414: throws SQLException {
415: return new Server(new TcpServer(), args);
416: }
417:
418: /**
419: * Create a new PG server, but does not start it yet.
420: * Example:
421: * <pre>
422: * Server server =
423: * Server.createPgServer(new String[]{
424: * "-pgAllowOthers", "true"}).start();
425: * </pre>
426: *
427: * @param args
428: * @return the server
429: */
430: public static Server createPgServer(String[] args)
431: throws SQLException {
432: return new Server(new PgServer(), args);
433: }
434:
435: /**
436: * Tries to start the server.
437: * @return the server if successful
438: * @throws SQLException if the server could not be started
439: */
440: public Server start() throws SQLException {
441: service.start();
442: Thread t = new Thread(this );
443: t.setName(service.getName() + " (" + service.getURL() + ")");
444: t.start();
445: for (int i = 1; i < 64; i += i) {
446: wait(i);
447: if (isRunning()) {
448: return this ;
449: }
450: }
451: throw Message.getSQLException(ErrorCode.CONNECTION_BROKEN);
452: }
453:
454: private static void wait(int i) {
455: try {
456: // sleep at most 4096 ms
457: long sleep = (long) i * (long) i;
458: Thread.sleep(sleep);
459: } catch (InterruptedException e) {
460: // ignore
461: }
462: }
463:
464: private void stopAll() {
465: if (web != null && web.isRunning()) {
466: web.stop();
467: web = null;
468: }
469: if (tcp != null && tcp.isRunning()) {
470: tcp.stop();
471: tcp = null;
472: }
473: if (pg != null && pg.isRunning()) {
474: pg.stop();
475: pg = null;
476: }
477: if (ftp != null && ftp.isRunning()) {
478: ftp.stop();
479: ftp = null;
480: }
481: }
482:
483: /**
484: * Checks if the server is running.
485: *
486: * @return if the server is running
487: */
488: public boolean isRunning() {
489: return service.isRunning();
490: }
491:
492: /**
493: * Stops the server.
494: */
495: public void stop() {
496: service.stop();
497: }
498:
499: /**
500: * Gets the URL of this server.
501: * @return the url
502: */
503: public String getURL() {
504: return service.getURL();
505: }
506:
507: private Server(Service service, String[] args) throws SQLException {
508: this .service = service;
509: try {
510: service.init(args);
511: } catch (Exception e) {
512: throw Message.convert(e);
513: }
514: }
515:
516: /**
517: * INTERNAL
518: */
519: public void run() {
520: try {
521: service.listen();
522: } catch (Exception e) {
523: TraceSystem.traceThrowable(e);
524: }
525: }
526:
527: /**
528: * INTERNAL
529: */
530: public void setShutdownHandler(ShutdownHandler shutdownHandler) {
531: this .shutdownHandler = shutdownHandler;
532: }
533:
534: /**
535: * INTERNAL
536: */
537: public void shutdown() {
538: if (shutdownHandler != null) {
539: shutdownHandler.shutdown();
540: } else {
541: stopAll();
542: }
543: }
544:
545: /**
546: * Get the service attached to this server.
547: *
548: * @return the service
549: */
550: public Service getService() {
551: return service;
552: }
553: }
|