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 | |
8 | //## AWT ## |
9 | import java.awt.Button; |
10 | import java.awt.Dimension; |
11 | import java.awt.Font; |
12 | import java.awt.Frame; |
13 | import java.awt.GraphicsEnvironment; |
14 | import java.awt.GridBagConstraints; |
15 | import java.awt.GridBagLayout; |
16 | import java.awt.Image; |
17 | import java.awt.Insets; |
18 | import java.awt.Label; |
19 | import java.awt.MenuItem; |
20 | import java.awt.Panel; |
21 | import java.awt.PopupMenu; |
22 | import java.awt.SystemColor; |
23 | import java.awt.TextField; |
24 | import java.awt.Toolkit; |
25 | import java.awt.event.ActionEvent; |
26 | import java.awt.event.ActionListener; |
27 | import java.awt.event.MouseEvent; |
28 | import java.awt.event.MouseListener; |
29 | import java.awt.event.WindowEvent; |
30 | import java.awt.event.WindowListener; |
31 | import java.io.IOException; |
32 | import java.sql.Connection; |
33 | import java.sql.SQLException; |
34 | import org.h2.server.ShutdownHandler; |
35 | import org.h2.util.JdbcUtils; |
36 | import org.h2.util.Tool; |
37 | import org.h2.util.Utils; |
38 | |
39 | /** |
40 | * Starts the H2 Console (web-) server, as well as the TCP and PG server. |
41 | * @h2.resource |
42 | * |
43 | * @author Thomas Mueller, Ridvan Agar |
44 | */ |
45 | public class Console extends Tool implements |
46 | //## AWT ## |
47 | ActionListener, MouseListener, WindowListener, |
48 | //*/ |
49 | ShutdownHandler { |
50 | |
51 | //## AWT ## |
52 | private Frame frame; |
53 | private boolean trayIconUsed; |
54 | private Font font; |
55 | private Button startBrowser; |
56 | private TextField urlText; |
57 | private Object tray; |
58 | private Object trayIcon; |
59 | //*/ |
60 | private Server web, tcp, pg; |
61 | private boolean isWindows; |
62 | private long lastOpen; |
63 | |
64 | /** |
65 | * When running without options, -tcp, -web, -browser and -pg are started. |
66 | * <br /> |
67 | * Options are case sensitive. Supported options are: |
68 | * <table> |
69 | * <tr><td>[-help] or [-?]</td> |
70 | * <td>Print the list of options</td></tr> |
71 | * <tr><td>[-url]</td> |
72 | * <td>Start a browser and connect to this URL</td></tr> |
73 | * <tr><td>[-driver]</td> |
74 | * <td>Used together with -url: the driver</td></tr> |
75 | * <tr><td>[-user]</td> |
76 | * <td>Used together with -url: the user name</td></tr> |
77 | * <tr><td>[-password]</td> |
78 | * <td>Used together with -url: the password</td></tr> |
79 | * <tr><td>[-web]</td> |
80 | * <td>Start the web server with the H2 Console</td></tr> |
81 | * <tr><td>[-tool]</td> |
82 | * <td>Start the icon or window that allows to start a browser</td></tr> |
83 | * <tr><td>[-browser]</td> |
84 | * <td>Start a browser connecting to the web server</td></tr> |
85 | * <tr><td>[-tcp]</td> |
86 | * <td>Start the TCP server</td></tr> |
87 | * <tr><td>[-pg]</td> |
88 | * <td>Start the PG server</td></tr> |
89 | * </table> |
90 | * For each Server, additional options are available; |
91 | * for details, see the Server tool.<br /> |
92 | * If a service can not be started, the program |
93 | * terminates with an exit code of 1. |
94 | * @h2.resource |
95 | * |
96 | * @param args the command line arguments |
97 | */ |
98 | public static void main(String... args) throws SQLException { |
99 | new Console().runTool(args); |
100 | } |
101 | |
102 | /** |
103 | * This tool starts the H2 Console (web-) server, as well as the TCP and PG |
104 | * server. For JDK 1.6, a system tray icon is created, for platforms that |
105 | * support it. Otherwise, a small window opens. |
106 | * |
107 | * @param args the command line arguments |
108 | */ |
109 | @Override |
110 | public void runTool(String... args) throws SQLException { |
111 | isWindows = Utils.getProperty("os.name", "").startsWith("Windows"); |
112 | boolean tcpStart = false, pgStart = false, webStart = false, toolStart = false; |
113 | boolean browserStart = false; |
114 | boolean startDefaultServers = true; |
115 | boolean printStatus = args != null && args.length > 0; |
116 | String driver = null, url = null, user = null, password = null; |
117 | boolean tcpShutdown = false, tcpShutdownForce = false; |
118 | String tcpPassword = ""; |
119 | String tcpShutdownServer = ""; |
120 | for (int i = 0; args != null && i < args.length; i++) { |
121 | String arg = args[i]; |
122 | if (arg == null) { |
123 | continue; |
124 | } else if ("-?".equals(arg) || "-help".equals(arg)) { |
125 | showUsage(); |
126 | return; |
127 | } else if ("-url".equals(arg)) { |
128 | startDefaultServers = false; |
129 | url = args[++i]; |
130 | } else if ("-driver".equals(arg)) { |
131 | driver = args[++i]; |
132 | } else if ("-user".equals(arg)) { |
133 | user = args[++i]; |
134 | } else if ("-password".equals(arg)) { |
135 | password = args[++i]; |
136 | } else if (arg.startsWith("-web")) { |
137 | if ("-web".equals(arg)) { |
138 | startDefaultServers = false; |
139 | webStart = true; |
140 | } else if ("-webAllowOthers".equals(arg)) { |
141 | // no parameters |
142 | } else if ("-webDaemon".equals(arg)) { |
143 | // no parameters |
144 | } else if ("-webSSL".equals(arg)) { |
145 | // no parameters |
146 | } else if ("-webPort".equals(arg)) { |
147 | i++; |
148 | } else { |
149 | showUsageAndThrowUnsupportedOption(arg); |
150 | } |
151 | } else if ("-tool".equals(arg)) { |
152 | startDefaultServers = false; |
153 | webStart = true; |
154 | toolStart = true; |
155 | } else if ("-browser".equals(arg)) { |
156 | startDefaultServers = false; |
157 | webStart = true; |
158 | browserStart = true; |
159 | } else if (arg.startsWith("-tcp")) { |
160 | if ("-tcp".equals(arg)) { |
161 | startDefaultServers = false; |
162 | tcpStart = true; |
163 | } else if ("-tcpAllowOthers".equals(arg)) { |
164 | // no parameters |
165 | } else if ("-tcpDaemon".equals(arg)) { |
166 | // no parameters |
167 | } else if ("-tcpSSL".equals(arg)) { |
168 | // no parameters |
169 | } else if ("-tcpPort".equals(arg)) { |
170 | i++; |
171 | } else if ("-tcpPassword".equals(arg)) { |
172 | tcpPassword = args[++i]; |
173 | } else if ("-tcpShutdown".equals(arg)) { |
174 | startDefaultServers = false; |
175 | tcpShutdown = true; |
176 | tcpShutdownServer = args[++i]; |
177 | } else if ("-tcpShutdownForce".equals(arg)) { |
178 | tcpShutdownForce = true; |
179 | } else { |
180 | showUsageAndThrowUnsupportedOption(arg); |
181 | } |
182 | } else if (arg.startsWith("-pg")) { |
183 | if ("-pg".equals(arg)) { |
184 | startDefaultServers = false; |
185 | pgStart = true; |
186 | } else if ("-pgAllowOthers".equals(arg)) { |
187 | // no parameters |
188 | } else if ("-pgDaemon".equals(arg)) { |
189 | // no parameters |
190 | } else if ("-pgPort".equals(arg)) { |
191 | i++; |
192 | } else { |
193 | showUsageAndThrowUnsupportedOption(arg); |
194 | } |
195 | } else if ("-properties".equals(arg)) { |
196 | i++; |
197 | } else if ("-trace".equals(arg)) { |
198 | // no parameters |
199 | } else if ("-ifExists".equals(arg)) { |
200 | // no parameters |
201 | } else if ("-baseDir".equals(arg)) { |
202 | i++; |
203 | } else { |
204 | showUsageAndThrowUnsupportedOption(arg); |
205 | } |
206 | } |
207 | if (startDefaultServers) { |
208 | webStart = true; |
209 | toolStart = true; |
210 | browserStart = true; |
211 | tcpStart = true; |
212 | pgStart = true; |
213 | } |
214 | if (tcpShutdown) { |
215 | out.println("Shutting down TCP Server at " + tcpShutdownServer); |
216 | Server.shutdownTcpServer(tcpShutdownServer, |
217 | tcpPassword, tcpShutdownForce, false); |
218 | } |
219 | SQLException startException = null; |
220 | boolean webRunning = false; |
221 | |
222 | if (url != null) { |
223 | Connection conn = JdbcUtils.getConnection(driver, url, user, password); |
224 | Server.startWebServer(conn); |
225 | } |
226 | |
227 | if (webStart) { |
228 | try { |
229 | web = Server.createWebServer(args); |
230 | web.setShutdownHandler(this); |
231 | web.start(); |
232 | if (printStatus) { |
233 | out.println(web.getStatus()); |
234 | } |
235 | webRunning = true; |
236 | } catch (SQLException e) { |
237 | printProblem(e, web); |
238 | startException = e; |
239 | } |
240 | } |
241 | |
242 | //## AWT ## |
243 | if (toolStart && webRunning && !GraphicsEnvironment.isHeadless()) { |
244 | loadFont(); |
245 | try { |
246 | if (!createTrayIcon()) { |
247 | showWindow(); |
248 | } |
249 | } catch (Exception e) { |
250 | e.printStackTrace(); |
251 | } |
252 | } |
253 | //*/ |
254 | |
255 | // start browser in any case (even if the server is already running) |
256 | // because some people don't look at the output, |
257 | // but are wondering why nothing happens |
258 | if (browserStart && web != null) { |
259 | openBrowser(web.getURL()); |
260 | } |
261 | |
262 | if (tcpStart) { |
263 | try { |
264 | tcp = Server.createTcpServer(args); |
265 | tcp.start(); |
266 | if (printStatus) { |
267 | out.println(tcp.getStatus()); |
268 | } |
269 | tcp.setShutdownHandler(this); |
270 | } catch (SQLException e) { |
271 | printProblem(e, tcp); |
272 | if (startException == null) { |
273 | startException = e; |
274 | } |
275 | } |
276 | } |
277 | if (pgStart) { |
278 | try { |
279 | pg = Server.createPgServer(args); |
280 | pg.start(); |
281 | if (printStatus) { |
282 | out.println(pg.getStatus()); |
283 | } |
284 | } catch (SQLException e) { |
285 | printProblem(e, pg); |
286 | if (startException == null) { |
287 | startException = e; |
288 | } |
289 | } |
290 | } |
291 | if (startException != null) { |
292 | shutdown(); |
293 | throw startException; |
294 | } |
295 | } |
296 | |
297 | private void printProblem(Exception e, Server server) { |
298 | if (server == null) { |
299 | e.printStackTrace(); |
300 | } else { |
301 | out.println(server.getStatus()); |
302 | out.println("Root cause: " + e.getMessage()); |
303 | } |
304 | } |
305 | |
306 | private static Image loadImage(String name) { |
307 | try { |
308 | byte[] imageData = Utils.getResource(name); |
309 | if (imageData == null) { |
310 | return null; |
311 | } |
312 | return Toolkit.getDefaultToolkit().createImage(imageData); |
313 | } catch (IOException e) { |
314 | e.printStackTrace(); |
315 | return null; |
316 | } |
317 | } |
318 | |
319 | /** |
320 | * INTERNAL. |
321 | * Stop all servers that were started using the console. |
322 | */ |
323 | @Override |
324 | public void shutdown() { |
325 | if (web != null && web.isRunning(false)) { |
326 | web.stop(); |
327 | web = null; |
328 | } |
329 | if (tcp != null && tcp.isRunning(false)) { |
330 | tcp.stop(); |
331 | tcp = null; |
332 | } |
333 | if (pg != null && pg.isRunning(false)) { |
334 | pg.stop(); |
335 | pg = null; |
336 | } |
337 | //## AWT ## |
338 | if (frame != null) { |
339 | frame.dispose(); |
340 | frame = null; |
341 | } |
342 | if (trayIconUsed) { |
343 | try { |
344 | // tray.remove(trayIcon); |
345 | Utils.callMethod(tray, "remove", trayIcon); |
346 | } catch (Exception e) { |
347 | // ignore |
348 | } finally { |
349 | trayIcon = null; |
350 | tray = null; |
351 | trayIconUsed = false; |
352 | } |
353 | System.gc(); |
354 | } |
355 | //*/ |
356 | // System.exit(0); |
357 | } |
358 | |
359 | //## AWT ## |
360 | private void loadFont() { |
361 | if (isWindows) { |
362 | font = new Font("Dialog", Font.PLAIN, 11); |
363 | } else { |
364 | font = new Font("Dialog", Font.PLAIN, 12); |
365 | } |
366 | } |
367 | |
368 | private boolean createTrayIcon() { |
369 | try { |
370 | // SystemTray.isSupported(); |
371 | boolean supported = (Boolean) Utils.callStaticMethod( |
372 | "java.awt.SystemTray.isSupported"); |
373 | if (!supported) { |
374 | return false; |
375 | } |
376 | PopupMenu menuConsole = new PopupMenu(); |
377 | MenuItem itemConsole = new MenuItem("H2 Console"); |
378 | itemConsole.setActionCommand("console"); |
379 | itemConsole.addActionListener(this); |
380 | itemConsole.setFont(font); |
381 | menuConsole.add(itemConsole); |
382 | MenuItem itemStatus = new MenuItem("Status"); |
383 | itemStatus.setActionCommand("status"); |
384 | itemStatus.addActionListener(this); |
385 | itemStatus.setFont(font); |
386 | menuConsole.add(itemStatus); |
387 | MenuItem itemExit = new MenuItem("Exit"); |
388 | itemExit.setFont(font); |
389 | itemExit.setActionCommand("exit"); |
390 | itemExit.addActionListener(this); |
391 | menuConsole.add(itemExit); |
392 | |
393 | // tray = SystemTray.getSystemTray(); |
394 | tray = Utils.callStaticMethod("java.awt.SystemTray.getSystemTray"); |
395 | |
396 | // Dimension d = tray.getTrayIconSize(); |
397 | Dimension d = (Dimension) Utils.callMethod(tray, "getTrayIconSize"); |
398 | String iconFile; |
399 | if (d.width >= 24 && d.height >= 24) { |
400 | iconFile = "/org/h2/res/h2-24.png"; |
401 | } else if (d.width >= 22 && d.height >= 22) { |
402 | // for Mac OS X 10.8.1 with retina display: |
403 | // the reported resolution is 22 x 22, but the image |
404 | // is scaled and the real resolution is 44 x 44 |
405 | iconFile = "/org/h2/res/h2-64-t.png"; |
406 | // iconFile = "/org/h2/res/h2-22-t.png"; |
407 | } else { |
408 | iconFile = "/org/h2/res/h2.png"; |
409 | } |
410 | Image icon = loadImage(iconFile); |
411 | |
412 | // trayIcon = new TrayIcon(image, "H2 Database Engine", |
413 | // menuConsole); |
414 | trayIcon = Utils.newInstance("java.awt.TrayIcon", |
415 | icon, "H2 Database Engine", menuConsole); |
416 | |
417 | // trayIcon.addMouseListener(this); |
418 | Utils.callMethod(trayIcon, "addMouseListener", this); |
419 | |
420 | // tray.add(trayIcon); |
421 | Utils.callMethod(tray, "add", trayIcon); |
422 | |
423 | this.trayIconUsed = true; |
424 | |
425 | return true; |
426 | } catch (Exception e) { |
427 | return false; |
428 | } |
429 | } |
430 | |
431 | private void showWindow() { |
432 | if (frame != null) { |
433 | return; |
434 | } |
435 | frame = new Frame("H2 Console"); |
436 | frame.addWindowListener(this); |
437 | Image image = loadImage("/org/h2/res/h2.png"); |
438 | if (image != null) { |
439 | frame.setIconImage(image); |
440 | } |
441 | frame.setResizable(false); |
442 | frame.setBackground(SystemColor.control); |
443 | |
444 | GridBagLayout layout = new GridBagLayout(); |
445 | frame.setLayout(layout); |
446 | |
447 | // the main panel keeps everything together |
448 | Panel mainPanel = new Panel(layout); |
449 | |
450 | GridBagConstraints constraintsPanel = new GridBagConstraints(); |
451 | constraintsPanel.gridx = 0; |
452 | constraintsPanel.weightx = 1.0D; |
453 | constraintsPanel.weighty = 1.0D; |
454 | constraintsPanel.fill = GridBagConstraints.BOTH; |
455 | constraintsPanel.insets = new Insets(0, 10, 0, 10); |
456 | constraintsPanel.gridy = 0; |
457 | |
458 | GridBagConstraints constraintsButton = new GridBagConstraints(); |
459 | constraintsButton.gridx = 0; |
460 | constraintsButton.gridwidth = 2; |
461 | constraintsButton.insets = new Insets(10, 0, 0, 0); |
462 | constraintsButton.gridy = 1; |
463 | constraintsButton.anchor = GridBagConstraints.EAST; |
464 | |
465 | GridBagConstraints constraintsTextField = new GridBagConstraints(); |
466 | constraintsTextField.fill = GridBagConstraints.HORIZONTAL; |
467 | constraintsTextField.gridy = 0; |
468 | constraintsTextField.weightx = 1.0; |
469 | constraintsTextField.insets = new Insets(0, 5, 0, 0); |
470 | constraintsTextField.gridx = 1; |
471 | |
472 | GridBagConstraints constraintsLabel = new GridBagConstraints(); |
473 | constraintsLabel.gridx = 0; |
474 | constraintsLabel.gridy = 0; |
475 | |
476 | Label label = new Label("H2 Console URL:", Label.LEFT); |
477 | label.setFont(font); |
478 | mainPanel.add(label, constraintsLabel); |
479 | |
480 | urlText = new TextField(); |
481 | urlText.setEditable(false); |
482 | urlText.setFont(font); |
483 | urlText.setText(web.getURL()); |
484 | if (isWindows) { |
485 | urlText.setFocusable(false); |
486 | } |
487 | mainPanel.add(urlText, constraintsTextField); |
488 | |
489 | startBrowser = new Button("Start Browser"); |
490 | startBrowser.setFocusable(false); |
491 | startBrowser.setActionCommand("console"); |
492 | startBrowser.addActionListener(this); |
493 | startBrowser.setFont(font); |
494 | mainPanel.add(startBrowser, constraintsButton); |
495 | frame.add(mainPanel, constraintsPanel); |
496 | |
497 | int width = 300, height = 120; |
498 | frame.setSize(width, height); |
499 | Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); |
500 | frame.setLocation((screenSize.width - width) / 2, |
501 | (screenSize.height - height) / 2); |
502 | try { |
503 | frame.setVisible(true); |
504 | } catch (Throwable t) { |
505 | // ignore |
506 | // some systems don't support this method, for example IKVM |
507 | // however it still works |
508 | } |
509 | try { |
510 | // ensure this window is in front of the browser |
511 | frame.setAlwaysOnTop(true); |
512 | frame.setAlwaysOnTop(false); |
513 | } catch (Throwable t) { |
514 | // ignore |
515 | } |
516 | } |
517 | |
518 | private void startBrowser() { |
519 | if (web != null) { |
520 | String url = web.getURL(); |
521 | if (urlText != null) { |
522 | urlText.setText(url); |
523 | } |
524 | long now = System.currentTimeMillis(); |
525 | if (lastOpen == 0 || lastOpen + 100 < now) { |
526 | lastOpen = now; |
527 | openBrowser(url); |
528 | } |
529 | } |
530 | } |
531 | //*/ |
532 | |
533 | private void openBrowser(String url) { |
534 | try { |
535 | Server.openBrowser(url); |
536 | } catch (Exception e) { |
537 | out.println(e.getMessage()); |
538 | } |
539 | } |
540 | |
541 | /** |
542 | * INTERNAL |
543 | */ |
544 | //## AWT ## |
545 | @Override |
546 | public void actionPerformed(ActionEvent e) { |
547 | String command = e.getActionCommand(); |
548 | if ("exit".equals(command)) { |
549 | shutdown(); |
550 | } else if ("console".equals(command)) { |
551 | startBrowser(); |
552 | } else if ("status".equals(command)) { |
553 | showWindow(); |
554 | } else if (startBrowser == e.getSource()) { |
555 | // for some reason, IKVM ignores setActionCommand |
556 | startBrowser(); |
557 | } |
558 | } |
559 | //*/ |
560 | |
561 | /** |
562 | * INTERNAL |
563 | */ |
564 | //## AWT ## |
565 | @Override |
566 | public void mouseClicked(MouseEvent e) { |
567 | if (e.getButton() == MouseEvent.BUTTON1) { |
568 | startBrowser(); |
569 | } |
570 | } |
571 | //*/ |
572 | |
573 | /** |
574 | * INTERNAL |
575 | */ |
576 | //## AWT ## |
577 | @Override |
578 | public void mouseEntered(MouseEvent e) { |
579 | // nothing to do |
580 | } |
581 | //*/ |
582 | |
583 | /** |
584 | * INTERNAL |
585 | */ |
586 | //## AWT ## |
587 | @Override |
588 | public void mouseExited(MouseEvent e) { |
589 | // nothing to do |
590 | } |
591 | //*/ |
592 | |
593 | /** |
594 | * INTERNAL |
595 | */ |
596 | //## AWT ## |
597 | @Override |
598 | public void mousePressed(MouseEvent e) { |
599 | // nothing to do |
600 | } |
601 | //*/ |
602 | |
603 | /** |
604 | * INTERNAL |
605 | */ |
606 | //## AWT ## |
607 | @Override |
608 | public void mouseReleased(MouseEvent e) { |
609 | // nothing to do |
610 | } |
611 | //*/ |
612 | |
613 | /** |
614 | * INTERNAL |
615 | */ |
616 | //## AWT ## |
617 | @Override |
618 | public void windowClosing(WindowEvent e) { |
619 | if (trayIconUsed) { |
620 | frame.dispose(); |
621 | frame = null; |
622 | } else { |
623 | shutdown(); |
624 | } |
625 | } |
626 | //*/ |
627 | |
628 | /** |
629 | * INTERNAL |
630 | */ |
631 | //## AWT ## |
632 | @Override |
633 | public void windowActivated(WindowEvent e) { |
634 | // nothing to do |
635 | } |
636 | //*/ |
637 | |
638 | /** |
639 | * INTERNAL |
640 | */ |
641 | //## AWT ## |
642 | @Override |
643 | public void windowClosed(WindowEvent e) { |
644 | // nothing to do |
645 | } |
646 | //*/ |
647 | |
648 | /** |
649 | * INTERNAL |
650 | */ |
651 | //## AWT ## |
652 | @Override |
653 | public void windowDeactivated(WindowEvent e) { |
654 | // nothing to do |
655 | } |
656 | //*/ |
657 | |
658 | /** |
659 | * INTERNAL |
660 | */ |
661 | //## AWT ## |
662 | @Override |
663 | public void windowDeiconified(WindowEvent e) { |
664 | // nothing to do |
665 | } |
666 | //*/ |
667 | |
668 | /** |
669 | * INTERNAL |
670 | */ |
671 | //## AWT ## |
672 | @Override |
673 | public void windowIconified(WindowEvent e) { |
674 | // nothing to do |
675 | } |
676 | //*/ |
677 | |
678 | /** |
679 | * INTERNAL |
680 | */ |
681 | //## AWT ## |
682 | @Override |
683 | public void windowOpened(WindowEvent e) { |
684 | // nothing to do |
685 | } |
686 | //*/ |
687 | |
688 | } |