Java tutorial
/* * Copyright (c) 2008-2016 Haulmont. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import org.apache.commons.lang.StringUtils; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.datatransfer.StringSelection; import java.awt.event.*; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; /** * UI monitor for HSQLDB server instance. * */ public class CubaHSQLDBServer extends JFrame { public static final String SERVER_CLASS = "org.hsqldb.server.Server"; public static void main(final String[] args) { final boolean validInit = args.length > 2; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { final CubaHSQLDBServer monitor = new CubaHSQLDBServer(); monitor.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); monitor.setLocationRelativeTo(null); monitor.setVisible(true); if (validInit) { Integer dbPort = Integer.valueOf(args[0]); String dbPath = args[1]; String dbName = args[2]; final HSQLServer server = monitor.startServer(dbPort, dbPath, dbName); if (server != null) { monitor.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { try { server.shutdownCatalogs(2 /* NORMAL CLOSE MODE */); } catch (RuntimeException exception) { // Ignore exceptions from server. } } }); } } else { String argStr = StringUtils.join(args, ' '); monitor.setStatus(String.format( "Invalid usage (args: '%s')\nExpected arguments: <port> <dbPath> <dbName>", argStr)); } } }); } private CubaHSQLDBServer() { Font monospaced = Font.decode("monospaced"); statusArea = new JTextArea(2, 80); statusArea.setFont(monospaced); statusArea.setMargin(new Insets(5, 5, 5, 5)); exceptionArea = new JTextArea(26, 80); exceptionArea.setFont(monospaced); exceptionArea.setMargin(new Insets(5, 5, 5, 5)); JPanel exceptionWrapperContainer = new JPanel(); exceptionWrapperContainer.setLayout(new BorderLayout(0, 0)); exceptionWrapperContainer.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); exceptionWrapperContainer.add(exceptionArea); JPanel statusWrapperContainer = new JPanel(); statusWrapperContainer.setLayout(new BorderLayout(0, 0)); statusWrapperContainer.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); statusWrapperContainer.add(statusArea); addCopyPopup(statusArea); addCopyPopup(exceptionArea); exceptionBox = new JPanel(); LayoutBuilder.create(exceptionBox, BoxLayout.Y_AXIS).addSpace(5).addComponent(exceptionWrapperContainer); LayoutBuilder.create(this.getContentPane(), BoxLayout.X_AXIS).addSpace(5).addContainer(BoxLayout.Y_AXIS) .addSpace(5).addComponent(statusWrapperContainer).addComponent(exceptionBox).addSpace(5) .returnToParent().addSpace(5); statusArea.setEditable(false); exceptionArea.setEditable(false); exceptionBox.setVisible(false); exceptionArea.setBackground(new Color(255, 255, 212)); this.pack(); this.setResizable(true); this.setTitle("HSQLDB Server"); try { this.setIconImage(ImageIO.read(getClass().getResourceAsStream("/icons/database.png"))); } catch (IOException e) { throw new IllegalStateException("Unable to find icon for HSQLDB window"); } } private void addCopyPopup(final JTextArea source) { final JPopupMenu popup = new JPopupMenu(); popup.add(new AbstractAction("Copy to clipboard") { @Override public void actionPerformed(ActionEvent e) { StringSelection contents = new StringSelection(source.getText()); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents, contents); } }); source.add(popup); source.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()) { popup.show(source, e.getX(), e.getY()); } } @Override public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) { popup.show(source, e.getX(), e.getY()); } } }); } public void setStatus(String status) { this.statusArea.setEditable(true); setTextPreserveSize(this.statusArea, status); this.statusArea.setEditable(false); } public void setException(Throwable exception) { this.exceptionBox.setVisible(true); this.exceptionArea.setEditable(true); if (exception != null) { StringWriter buffer = new StringWriter(); PrintWriter writer = new PrintWriter(buffer); exception.printStackTrace(writer); setTextPreserveSize(this.exceptionArea, buffer.toString()); } else { this.exceptionArea.setText(null); } this.exceptionArea.setEditable(false); this.pack(); } private void setTextPreserveSize(JTextArea target, String text) { Dimension size = target.getPreferredSize(); target.setText(text); target.setPreferredSize(size); } public HSQLServer startServer(int dbPort, String dbPath, String dbName) { try { Class<?> serverClass = Class.forName(SERVER_CLASS); HSQLServer server = ServerObjectProxy.newInstance(serverClass); server.setDaemon(true); server.setPort(dbPort); server.setDatabaseName(0, dbName); server.setDatabasePath(0, getDbPath(dbPath, dbName)); server.start(); String format = String.format("Status: %s Port: %s\nDB name: '%s' DB path: '%s'", "%s", server.getPort(), dbName, dbPath); ServerStatusChecker checker = new ServerStatusChecker(this, server, format); checker.schedule(); return server; } catch (InstantiationException | IllegalAccessException | RuntimeException e) { setStatus("Failed to start the server due to: " + e.getClass().getCanonicalName()); setException(e); return null; } catch (ClassNotFoundException e) { setStatus("Check build configuration to ensure that hsql driver is present in classpath"); setException(e); return null; } } private String getDbPath(String dbPath, String dbName) { File dbDir = new File(dbPath, dbName); return new File(dbDir, dbName).getAbsolutePath(); } private JTextArea statusArea; private JTextArea exceptionArea; private JPanel exceptionBox; /** * org.hsqldb.server.Server real method signatures used for object proxy. */ private interface HSQLServer { int getPort(); void setPort(int port); int getState(); Throwable getServerError(); void setDaemon(boolean daemon); void setDatabaseName(int index, String name); void setDatabasePath(int index, String path); void shutdownCatalogs(int mode); void start(); } private static class LayoutBuilder { public static LayoutBuilder create(Container container, int axis) { return new LayoutBuilder(container, axis, null); } private LayoutBuilder(Container container, int axis, LayoutBuilder parent) { this.axis = axis; this.parent = parent; this.container = container; //noinspection MagicConstant this.container.setLayout(new BoxLayout(this.container, axis)); } public LayoutBuilder addSpace(int pixels) { Dimension dimension = new Dimension(0, 0); switch (axis) { case BoxLayout.PAGE_AXIS: case BoxLayout.LINE_AXIS: if (container.getComponentOrientation().isHorizontal()) { dimension.setSize(pixels, 0); } else { dimension.setSize(0, pixels); } break; case BoxLayout.X_AXIS: dimension.setSize(pixels, 0); break; case BoxLayout.Y_AXIS: dimension.setSize(0, pixels); } container.add(Box.createRigidArea(dimension)); return this; } public LayoutBuilder addContainer(int axis) { JPanel panel = new JPanel(); container.add(panel); return new LayoutBuilder(panel, axis, this); } public LayoutBuilder addComponent(Component component) { container.add(component); return this; } public LayoutBuilder returnToParent() { return parent; } private LayoutBuilder parent; private Container container; private int axis; } private static class ServerObjectProxy implements InvocationHandler { public static HSQLServer newInstance(Class<?> server) throws IllegalAccessException, InstantiationException { return (HSQLServer) Proxy.newProxyInstance(server.getClassLoader(), new Class[] { HSQLServer.class }, new ServerObjectProxy(server)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); Method target = methods.get(name); if (target == null) { target = serverCls.getMethod(name, method.getParameterTypes()); methods.put(name, target); } return target.invoke(serverImpl, args); } private Object serverImpl; private Class<?> serverCls; private Map<String, Method> methods = new HashMap<>(); private ServerObjectProxy(Class<?> serverCls) throws IllegalAccessException, InstantiationException { this.serverCls = serverCls; this.serverImpl = serverCls.newInstance(); } } private static class ServerStatusChecker extends TimerTask { public void schedule() { timer.schedule(this, 1000, 1000); } private ServerStatusChecker(CubaHSQLDBServer monitor, HSQLServer server, String statusFormat) { this.monitor = monitor; this.server = server; this.statusFormat = statusFormat; this.serverStatuses = new HashMap<Integer, String>() { { put(0, "SC_DATABASE_SHUTDOWN"); put(1, "SERVER_STATE_ONLINE"); put(4, "SERVER_STATE_OPENING"); put(8, "SERVER_STATE_CLOSING"); put(16, "SERVER_STATE_SHUTDOWN"); } }; } @Override public void run() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { int state = server.getState(); if (state == 16) { Throwable exception = server.getServerError(); if (exception != null) { monitor.setException(exception); timer.cancel(); } } monitor.setStatus(String.format(statusFormat, serverStatuses.get(state))); } catch (RuntimeException e) { monitor.setStatus("Runtime exception"); monitor.setException(e); timer.cancel(); } } }); } private Timer timer = new Timer(true); private HSQLServer server; private CubaHSQLDBServer monitor; private Map<Integer, String> serverStatuses; private String statusFormat; } }