All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hsqldb.server.Server Maven / Gradle / Ivy

There is a newer version: 2.7.4
Show newest version
/* Copyright (c) 2001-2021, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb.server;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.sql.DriverManager;
import java.util.Enumeration;
import java.util.Properties;
import java.util.StringTokenizer;

import org.hsqldb.Database;
import org.hsqldb.DatabaseManager;
import org.hsqldb.DatabaseURL;
import org.hsqldb.HsqlDateTime.SystemTimeString;
import org.hsqldb.HsqlException;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.FileUtil;
import org.hsqldb.lib.HashSet;
import org.hsqldb.lib.IntKeyHashMap;
import org.hsqldb.lib.Iterator;
import org.hsqldb.lib.Notified;
import org.hsqldb.lib.StopWatch;
import org.hsqldb.lib.StringUtil;
import org.hsqldb.persist.HsqlDatabaseProperties;
import org.hsqldb.persist.HsqlProperties;
import org.hsqldb.resources.ResourceBundleHandler;
import org.hsqldb.result.Result;
import org.hsqldb.result.ResultConstants;

// fredt@users 20020215 - patch 1.7.0
// methods reorganised to use new HsqlProperties class
// fredt@users 20020424 - patch 1.7.0 - shutdown without exit
// see the comments in ServerConnection.java
// unsaved@users 20021113 - patch 1.7.2 - SSL support
// campbell-burnet@users 20030510-14 - 1.7.2 - SSL support moved to factory interface
// campbell-burnet@users 20030510-14 - 1.7.2 - service control, JavaBean API
// fredt@users 20030916 - 1.7.2 - review, simplification and multiple DB's
// fredt@users 20040320 - 1.7.2 - review and correction
// fredt@users 20050225 - 1.8.0 - minor corrections
// fredt@users 20051231 - 1.8.1 - support for remote opening of databases
// fredt@users 20080531 - 1.8.1 - removed synchronized from print methods
// unnecessary and could cause deadlock

/**
 * The HyperSQL hsql: and hsqls: protocol network database server. 

* * A Server object acts as a network database server and is one way of using * the client-server mode of HSQLDB Database Engine. Instances of this * class handle native HSQL protocol connections exclusively, allowing database * queries to be performed efficiently across the network. Server's direct * descendant, WebServer, handles HTTP protocol connections exclusively, * allowing HSQL protocol to be tunnelled over HTTP to avoid sandbox and * firewall issues, albeit less efficiently.

* * There are a number of ways to configure and start a Server instance.

* * When started from the command line or programatically via the main(String[]) * method, configuration occurs in three phases, with later phases overriding * properties set by previous phases: * *

    *
  1. Upon construction, a Server object is assigned a set of default * properties. * *
  2. If it exists, properties are loaded from a file named * 'server.properties' in the present working directory. * *
  3. The command line arguments (alternatively, the String[] passed to * main()) are parsed and used to further configure the Server's * properties. * *

* * From the command line, the options are as follows: *

 * +-----------------+-------------+----------+------------------------------+
 * |    OPTION       |    TYPE     | DEFAULT  |         DESCRIPTION          |
 * +-----------------+-------------+----------+------------------------------|
 * | --help          |             |          | prints this message          |
 * | --address       | name|number | any      | server inet address          |
 * | --port          | number      | 9001/544 | port at which server listens |
 * | --database.i    | [type]spec  | 0=test   | path of database i           |
 * | --dbname.i      | alias       |          | url alias for database i     |
 * | --silent        | true|false  | true     | false => display all queries |
 * | --trace         | true|false  | false    | display JDBC trace messages  |
 * | --tls           | true|false  | false    | TLS/SSL (secure) sockets     |
 * | --no_system_exit| true|false  | false    | do not issue System.exit()   |
 * | --remote_open   | true|false  | false    | can open databases remotely  |
 * | --props         | filepath    |          | file path of properties file |
 * +-----------------+-------------+----------+------------------------------+
 * 
* * The database.i and dbname.i options need further * explanation: * *
    *
  • Multiple databases can be served by each instance of the Server. * The value of i is currently limited to the range 0..9, * allowing up to 10 different databases. Any number is this range * can be used. * *
  • The value assigned to database.i is interpreted using the * format '[type]spec', where the optional type component * is one of 'file:', 'res:' or 'mem:' and the * spec component is interpreted in the context of the * type component.

    * * If omitted, the type component is taken to be * 'file:'.

    * * A full description of how * '[type]spec' values are interpreted appears in the overview for * {@link org.hsqldb.jdbc.JDBCConnection JDBCConnection}. * *

  • The value assigned to dbname.i is taken to be the key used to * look up the desired database instance and thus corresponds to the * <alias> component of the HSQLDB HSQL protocol database * connection url: * 'jdbc:hsqldb:hsql[s]://host[port][/<alias>]'. * *
  • The value of database.0 is special. If dbname.0 * is not specified, then this defaults to an empty string and * a connection is made to database.0 path when * the <alias> component of an HSQLDB HSQL protocol database * connection url is omitted. If a database key/value pair is * found in the properties when the main method is called, this * pair is supersedes the database.0 setting

    * * This behaviour allows the previous * database connection url format to work with essentially unchanged * semantics. * *

  • When the remote_open property is true, a connection attempt * to an unopened database results in the database being opened. The URL * for connection should include the property filepath to specify the path. * 'jdbc:hsqldb:hsql[s]://host[port]/<alias>;filepath=hsqldb:file:<database path>'. * the given alias and filepath value will be associated together. The * database user and password to start this connection must be valid. * If this form of connection is used again, after the database has been * opened, the filepath property is ignored. * *
  • Once an alias such as "mydb" has been associated with a path, it cannot * be reassigned to a different path. * *
  • If a database is closed with the SHUTDOWN command, its * alias is removed. It is then possible to connect to this database again * with a different (or the same) alias. * *
  • If the same database is connected to via two different * aliases, and then one of the is closed with the SHUTDOWN command, the * other is also closed.

    *

* * From the 'server.properties' file, options can be set similarly, using a * slightly different format.

* * Here is an example 'server.properties' file: * *

 * server.port=9001
 * server.database.0=test
 * server.dbname.0=...
 * ...
 * server.database.n=...
 * server.dbname.n=...
 * server.silent=true
 * 
* * Starting with 1.7.2, Server has been refactored to become a simple JavaBean * with non-blocking start() and stop() service methods. It is possible to * configure a Server instance through the JavaBean API as well, but this * part of the public interface is still under review and will not be finalized * or documented fully until the final 1.7.2 release.

* * Note:

* * The 'no_system_exit' property is of particular interest.

* * If a Server instance is to run embedded in, say, an application server, * such as when the JDBCDataSource or HsqlServerFactory classes are used, it * is typically necessary to avoid calling System.exit() when the Server * instance shuts down.

* * By default, 'no_system_exit' is set: * *

    *
  1. true when a Server is started directly from the start() * method. * *
  2. false when a Server is started from the main(String[]) * method. *

* * These values are natural to their context because the first case allows * the JVM to exit by default on Server shutdown when a Server instance is * started from a command line environment, whereas the second case prevents * a typically unwanted JVM exit on Server shutdown when a Server instance * is started as part of a larger framework.

* * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.6.0 * @since 1.7.2 */ public class Server implements HsqlSocketRequestHandler, Notified { // protected static final int serverBundleHandle = ResourceBundleHandler.getBundleHandle( "org_hsqldb_server_Server_messages", null); // ServerProperties serverProperties; // HashSet serverConnSet; final Object serverConnSetSync = new Object(); // As of HSQLDB 1.9.0, the following arrays are used starting from 0. // The indexes do not correspond to the user-specified indexes. protected String[] dbAlias; protected String[] dbType; protected String[] dbPath; protected HsqlProperties[] dbProps; protected int[] dbID; protected long[] dbActionSequence; // set of aliases HashSet aliasSet = new HashSet(); // Currently unused protected int maxConnections; volatile long actionSequence; // protected String serverId; protected int serverProtocol; protected ThreadGroup serverConnectionThreadGroup; protected HsqlSocketFactory socketFactory; protected volatile ServerSocket socket; // private Thread serverThread; private Throwable serverError; private volatile int serverState; private volatile boolean isSilent; protected volatile boolean isRemoteOpen; protected boolean isDaemon; private PrintWriter logWriter; private PrintWriter errWriter; private ServerAcl acl = null; // null means no access tests private volatile boolean isShuttingDown; private SystemTimeString sysTime = new SystemTimeString(); /** * A specialized Thread inner class in which the run() method of this * server executes. */ private class ServerThread extends Thread { /** * Constructs a new thread in which to execute the run method * of this server. * * @param name The thread name */ ServerThread(String name) { super(name); setName(name + '@' + Integer.toString(Server.this.hashCode(), 16)); } /** * Executes the run() method of this server */ public void run() { Server.this.run(); printWithThread("ServerThread.run() exited"); } } /** * Returns thread object for "HSQLDB Server" thread * @return thread */ public Thread getServerThread() { return serverThread; } /** * Creates a new Server instance handling HSQL protocol connections. */ public Server() { this(ServerConstants.SC_PROTOCOL_HSQL); } /** * Creates a new Server instance handling the specified connection * protocol.

* * For example, the no-args WebServer constructor invokes this constructor * with ServerConstants.SC_PROTOCOL_HTTP, while the Server() no args * constructor invokes this constructor with * ServerConstants.SC_PROTOCOL_HSQL.

* * @param protocol the ServerConstants code indicating which * connection protocol to handle */ protected Server(int protocol) { init(protocol); } /** * Checks if this Server object is or is not running and throws if the * current state does not match the specified value. * * @param running if true, ensure the server is running, else ensure the * server is not running * @throws HsqlException if the supplied value does not match the * current running status */ public void checkRunning(boolean running) { int state; boolean error; printWithThread("checkRunning(" + running + ") entered"); state = getState(); error = (running && state != ServerConstants.SERVER_STATE_ONLINE) || (!running && state != ServerConstants.SERVER_STATE_SHUTDOWN); if (error) { String msg = "server is " + (running ? "not " : "") + "running"; throw Error.error(ErrorCode.GENERAL_ERROR, msg); } printWithThread("checkRunning(" + running + ") exited"); } /** * Returns true if this server is not running * * @return boolean */ public boolean isNotRunning() { int state = getState(); return state == ServerConstants.SERVER_STATE_SHUTDOWN; } /** * Closes all connections to this Server. */ public synchronized void signalCloseAllServerConnections() { Iterator it; ServerConnection[] array; printWithThread("signalCloseAllServerConnections() entered"); synchronized (serverConnSetSync) { // snapshot array = new ServerConnection[serverConnSet.size()]; serverConnSet.toArray(array); } for (int i = 0; i < array.length; i++) { ServerConnection sc = array[i]; printWithThread("Closing " + sc); // also removes all but one connection from serverConnSet sc.signalClose(); } printWithThread("signalCloseAllServerConnections() exited"); } /** * Retrieves, in string form, this server's host address. * * @return this server's host address */ public String getAddress() { return socket == null ? serverProperties.getProperty(ServerProperties.sc_key_address) : socket.getInetAddress().getHostAddress(); } /** * Retrieves the url alias (network name) of the i'th database * that this Server hosts. * * @param index the index of the url alias upon which to report * @param asconfigured if true, report the configured value, else * the live value * @return the url alias component of the i'th database * that this Server hosts, or null if no such name exists. */ public String getDatabaseName(int index, boolean asconfigured) { if (asconfigured) { return serverProperties.getProperty(ServerProperties.sc_key_dbname + "." + index); } else if (getState() == ServerConstants.SERVER_STATE_ONLINE) { return (dbAlias == null || index < 0 || index >= dbAlias.length) ? null : dbAlias[index]; } else { return null; } } /** * Retrieves the HSQLDB path descriptor (uri) of the i'th * Database that this Server hosts. * * @param index the index of the uri upon which to report * @param asconfigured if true, report the configured value, else * the live value * @return the HSQLDB database path descriptor of the i'th database * that this Server hosts, or null if no such path descriptor * exists */ public String getDatabasePath(int index, boolean asconfigured) { if (asconfigured) { return serverProperties.getProperty( ServerProperties.sc_key_database + "." + index); } else if (getState() == ServerConstants.SERVER_STATE_ONLINE) { return (dbPath == null || index < 0 || index >= dbPath.length) ? null : dbPath[index]; } else { return null; } } public String getDatabaseType(int index) { return (dbType == null || index < 0 || index >= dbType.length) ? null : dbType[index]; } /** * Retrieves the name of the web page served when no page is specified. * This attribute is relevant only when server protocol is HTTP(S). * * @return the name of the web page served when no page is specified */ public String getDefaultWebPage() { return "[IGNORED]"; } /** * Retrieves a String object describing the command line and * properties options for this Server. * * @return the command line and properties options help for this Server */ public String getHelpString() { return ResourceBundleHandler.getString(serverBundleHandle, "server.help"); } /** * Retrieves the PrintWriter to which server errors are printed. * * @return the PrintWriter to which server errors are printed. */ public PrintWriter getErrWriter() { return errWriter; } /** * Retrieves the PrintWriter to which server messages are printed. * * @return the PrintWriter to which server messages are printed. */ public PrintWriter getLogWriter() { return logWriter; } /** * Retrieves this server's host port. * * @return this server's host port */ public int getPort() { return serverProperties.getIntegerProperty( ServerProperties.sc_key_port, ServerConfiguration.getDefaultPort(serverProtocol, isTls())); } /** * Retrieves this server's product name.

* * Typically, this will be something like: "HSQLDB xxx server". * * @return the product name of this server */ public String getProductName() { return "HSQLDB server"; } /** * Retrieves the server's product version, as a String.

* * Typically, this will be something like: "1.x.x" or "2.x.x" and so on. * * @return the product version of the server */ public String getProductVersion() { return HsqlDatabaseProperties.THIS_VERSION; } /** * Retrieves a string resresentaion of the network protocol * this server offers, typically one of 'HTTP', HTTPS', 'HSQL' or 'HSQLS'. * * @return string representation of this server's protocol */ public String getProtocol() { return isTls() ? "HSQLS" : "HSQL"; } /** * Retrieves a Throwable indicating the last server error, if any.

* * @return a Throwable indicating the last server error */ public Throwable getServerError() { return serverError; } /** * Retrieves a String identifying this Server object. * * @return a String identifying this Server object */ public String getServerId() { return serverId; } /** * Retrieves current state of this server in numerically coded form.

* * Typically, this will be one of: * *

    *
  1. ServerProperties.SERVER_STATE_ONLINE (1) *
  2. ServerProperties.SERVER_STATE_OPENING (4) *
  3. ServerProperties.SERVER_STATE_CLOSING (8) *
  4. ServerProperties.SERVER_STATE_SHUTDOWN (16) *
* * @return this server's state code. */ public int getState() { return serverState; } /** * Retrieves a character sequence describing this server's current state, * including the message of the last exception, if there is one and it * is still in context. * * @return this server's state represented as a character sequence. */ public String getStateDescriptor() { String state; switch (serverState) { case ServerConstants.SERVER_STATE_SHUTDOWN : state = "SHUTDOWN"; break; case ServerConstants.SERVER_STATE_OPENING : state = "OPENING"; break; case ServerConstants.SERVER_STATE_CLOSING : state = "CLOSING"; break; case ServerConstants.SERVER_STATE_ONLINE : state = "ONLINE"; break; default : state = "UNKNOWN"; break; } return state; } /** * Retrieves the root context (directory) from which web content * is served. This property is relevant only when the server * protocol is HTTP(S). Although unlikely, it may be that in the future * other contexts, such as jar urls may be supported, so that pages can * be served from the contents of a jar or from the JVM class path. * * @return the root context (directory) from which web content is served */ public String getWebRoot() { return "[IGNORED]"; } /** * Assigns the specified socket to a new connection handler and * starts the handler in a new Thread. * * @param s the socket to connect */ public void handleConnection(Socket s) { Thread t; Runnable r; String ctn; printWithThread("handleConnection(" + s + ") entered"); if (!allowConnection(s)) { try { s.close(); } catch (Exception e) {} printWithThread("allowConnection(): connection refused"); printWithThread("handleConnection() exited"); return; } // Maybe set up socket options, SSL // Session tracing/callbacks, etc. if (socketFactory != null) { socketFactory.configureSocket(s); } if (serverProtocol == ServerConstants.SC_PROTOCOL_HSQL) { r = new ServerConnection(s, this); ctn = ((ServerConnection) r).getConnectionThreadName(); } else { r = new WebServerConnection(s, (WebServer) this); ctn = ((WebServerConnection) r).getConnectionThreadName(); } t = new Thread(serverConnectionThreadGroup, r, ctn); t.start(); printWithThread("handleConnection() exited"); } /** * Retrieves whether this server calls System.exit() when shutdown. * * @return true if this server does not call System.exit() */ public boolean isNoSystemExit() { return serverProperties.isPropertyTrue( ServerProperties.sc_key_no_system_exit); } /** * Retrieves whether this server restarts on shutdown. * * @return true this server restarts on shutdown */ public boolean isRestartOnShutdown() { return serverProperties.isPropertyTrue( ServerProperties.sc_key_autorestart_server); } /** * Retrieves whether silent mode operation was requested in * the server properties. * * @return if true, silent mode was requested, else trace messages * are to be printed */ public boolean isSilent() { return isSilent; } /** * Retrieves whether the use of secure sockets was requested in the * server properties. * * @return if true, secure sockets are requested, else not */ public boolean isTls() { return serverProperties.isPropertyTrue(ServerProperties.sc_key_tls); } /** * Retrieves whether JDBC trace messages are to go to System.out or the * DriverManger PrintStream/PrintWriter, if any. * * @return true if tracing is on (JDBC trace messages to system out) */ public boolean isTrace() { return serverProperties.isPropertyTrue(ServerProperties.sc_key_trace); } /** * Attempts to put properties from the file * with the specified path. The file * extension '.properties' is implicit and should not * be included in the path specification. * * @param path the path of the desired properties file, without the * '.properties' file extension * @throws HsqlException if this server is running * @return true if the indicated file was read successfully, else false */ public boolean putPropertiesFromFile(String path) { return putPropertiesFromFile(path, ".properties"); } /** * Attempts to put properties from the file with given extension. * * @param path the path of the desired properties file. * @param extension extension to add to path * @throws HsqlException if this server is running * @return true if the indicated file was read successfully, else false * */ public boolean putPropertiesFromFile(String path, String extension) { if (getState() != ServerConstants.SERVER_STATE_SHUTDOWN) { throw Error.error(ErrorCode.GENERAL_ERROR, "server properties"); } path = FileUtil.getFileUtil().canonicalOrAbsolutePath(path); ServerProperties p = ServerConfiguration.getPropertiesFromFile( ServerConstants.SC_PROTOCOL_HSQL, path, extension); if (p == null || p.isEmpty()) { return false; } printWithThread("putPropertiesFromFile(): [" + path + ".properties]"); try { setProperties(p); } catch (Exception e) { throw Error.error(e, ErrorCode.GENERAL_ERROR, ErrorCode.M_Message_Pair, new String[]{ "Failed to set properties" }); } return true; } /** * Puts properties from the supplied string argument. The relevant * key value pairs are the same as those for the (web)server.properties * file format, except that the 'server.' prefix should not be specified. * * @param s semicolon-delimited key=value pair string, * e.g. silent=false;port=8080;... * @throws HsqlException if this server is running */ public void putPropertiesFromString(String s) { if (getState() != ServerConstants.SERVER_STATE_SHUTDOWN) { throw Error.error(ErrorCode.GENERAL_ERROR); } if (StringUtil.isEmpty(s)) { return; } printWithThread("putPropertiesFromString(): [" + s + "]"); HsqlProperties p = HsqlProperties.delimitedArgPairsToProps(s, "=", ";", ServerProperties.sc_key_prefix); try { setProperties(p); } catch (Exception e) { throw Error.error(e, ErrorCode.GENERAL_ERROR, ErrorCode.M_Message_Pair, new String[]{ "Failed to set properties" }); } } /** * Sets the InetAddress with which this server's ServerSocket will be * constructed. A null or empty string or the special value "0.0.0.0" * can be used to bypass explicit selection, causing the ServerSocket * to be constructed without specifying an InetAddress. * * @param address A string representing the desired InetAddress as would * be retrieved by InetAddres.getByName(), or a null or empty string * or "0.0.0.0" to signify that the server socket should be constructed * using the signature that does not specify the InetAddress. * @throws HsqlException if this server is running */ public void setAddress(String address) { checkRunning(false); if (StringUtil.isEmpty(address)) { address = ServerConstants.SC_DEFAULT_ADDRESS; } printWithThread("setAddress(" + address + ")"); serverProperties.setProperty(ServerProperties.sc_key_address, address); } /** * Sets the external name (url alias) of the i'th hosted database. * * @param index int * @param name external name (url alias) of the i'th HSQLDB database * instance this server is to host. */ public void setDatabaseName(int index, String name) { checkRunning(false); printWithThread("setDatabaseName(" + index + "," + name + ")"); serverProperties.setProperty(ServerProperties.sc_key_dbname + "." + index, name); } /** * Sets the path of the hosted database. The path always starts with the * catalog type. Examples of the path include: "file:mydir/mydb", * "mem:mymemdb", "res:org/mydomain/mydbs/settingsdb". * * @param index int * @param path The path of the i'th HSQLDB database instance this server is * to host. */ public void setDatabasePath(int index, String path) { checkRunning(false); printWithThread("setDatabasePath(" + index + "," + path + ")"); serverProperties.setProperty(ServerProperties.sc_key_database + "." + index, path); } /** * Sets the name of the web page served when no page is specified. * * @param file the name of the web page served when no page is specified */ public void setDefaultWebPage(String file) { checkRunning(false); printWithThread("setDefaultWebPage(" + file + ")"); if (serverProtocol != ServerConstants.SC_PROTOCOL_HTTP) { return; } serverProperties.setProperty(ServerProperties.sc_key_web_default_page, file); } /** * Sets the server listen port. * * @param port the port at which this server listens */ public void setPort(int port) { checkRunning(false); printWithThread("setPort(" + port + ")"); serverProperties.setProperty(ServerProperties.sc_key_port, port); } /** * Sets the PrintWriter to which server errors are logged.

* * Setting this attribute to null disables server error logging * * @param pw the PrintWriter to which server messages are logged */ public void setErrWriter(PrintWriter pw) { errWriter = pw; } /** * Sets the PrintWriter to which server messages are logged.

* * Setting this attribute to null disables server message logging * * @param pw the PrintWriter to which server messages are logged */ public void setLogWriter(PrintWriter pw) { logWriter = pw; } /** * Sets whether this server calls System.exit() when shutdown. * * @param noExit if true, System.exit() will not be called. */ public void setNoSystemExit(boolean noExit) { printWithThread("setNoSystemExit(" + noExit + ")"); serverProperties.setProperty(ServerProperties.sc_key_no_system_exit, noExit); } /** * Sets whether this server restarts on shutdown. * * @param restart if true, this server restarts on shutdown */ public void setRestartOnShutdown(boolean restart) { printWithThread("setRestartOnShutdown(" + restart + ")"); serverProperties.setProperty( ServerProperties.sc_key_autorestart_server, restart); } /** * Sets silent mode operation * * @param silent if true, then silent mode, else trace messages * are to be printed */ public void setSilent(boolean silent) { serverProperties.setProperty(ServerProperties.sc_key_silent, silent); isSilent = silent; printWithThread("setSilent(" + silent + ")"); } /** * Sets whether to use secure sockets * * @param tls true for secure sockets, else false * @throws HsqlException if this server is running */ public void setTls(boolean tls) { checkRunning(false); printWithThread("setTls(" + tls + ")"); serverProperties.setProperty(ServerProperties.sc_key_tls, tls); } /** * Sets whether trace messages go to System.out or the * DriverManger PrintStream/PrintWriter, if any. * * @param trace if true, route JDBC trace messages to System.out */ public void setTrace(boolean trace) { printWithThread("setTrace(" + trace + ")"); serverProperties.setProperty(ServerProperties.sc_key_trace, trace); setLogToSystem(trace); } /** * No-op as deprecated. * * @param daemon ignored */ public void setDaemon(boolean daemon) { checkRunning(false); printWithThread("setDaemon(" + daemon + ")"); serverProperties.setProperty(ServerProperties.sc_key_daemon, daemon); } /** * Sets the path of the root directory from which web content is served. * * @param root the root (context) directory from which web content * is served */ public void setWebRoot(String root) { checkRunning(false); root = (new File(root)).getAbsolutePath(); printWithThread("setWebRoot(" + root + ")"); if (serverProtocol != ServerConstants.SC_PROTOCOL_HTTP) { return; } serverProperties.setProperty(ServerProperties.sc_key_web_root, root); } /** * Sets server properties using the specified properties object * * @param props The object containing properties to set * @throws ServerAcl.AclFormatException * ACL list was requested but problem loading ACL. * @throws IOException * ACL list was requested but I/O problem loading ACL. */ public void setProperties(Properties props) throws IOException, ServerAcl.AclFormatException { HsqlProperties serverProps = new HsqlProperties(props); setProperties(serverProps); } /** * Sets server properties using the specified HsqlProperties object * * @param props The HsqlProperties object containing properties to set * @throws ServerAcl.AclFormatException * ACL list was requested but problem loading ACL. * @throws IOException * ACL list was requested but I/O problem loading ACL. */ public void setProperties(HsqlProperties props) throws IOException, ServerAcl.AclFormatException { if (!isNotRunning()) { checkRunning(false); } if (props != null) { props.validate(); String[] errors = props.getErrorKeys(); if (errors.length > 0) { throw Error.error(ErrorCode.SERVER_NO_DATABASE, errors[0]); } serverProperties.addProperties(props); } maxConnections = serverProperties.getIntegerProperty( ServerProperties.sc_key_max_connections, 16); setLogToSystem(isTrace()); isSilent = serverProperties.isPropertyTrue(ServerProperties.sc_key_silent); isRemoteOpen = serverProperties.isPropertyTrue( ServerProperties.sc_key_remote_open_db); isDaemon = serverProperties.isPropertyTrue(ServerProperties.sc_key_daemon); String aclFilepath = serverProperties.getProperty(ServerProperties.sc_key_acl); if (aclFilepath != null) { acl = new ServerAcl(new File(aclFilepath)); if (logWriter != null && !isSilent) { acl.setPrintWriter(logWriter); } } } /** * Starts this server synchronously.

* * This method waits for current state to change from * SERVER_STATE_OPENNING. In order to discover the success or failure * of this operation, server state must be polled or a subclass of Server * must be used that overrides the setState method to provide state * change notification. * * @return the server state noted at entry to this method */ public int start() { printWithThread("start() entered"); int previousState = getState(); if (serverThread != null) { printWithThread("start(): serverThread != null; no action taken"); return previousState; } setState(ServerConstants.SERVER_STATE_OPENING); serverThread = new ServerThread("HSQLDB Server "); if (isDaemon) { serverThread.setDaemon(true); } serverThread.start(); // call synchronized getState() to become owner of the Server Object's monitor while (getState() == ServerConstants.SERVER_STATE_OPENING) { try { Thread.sleep(100); } catch (InterruptedException e) {} } printWithThread("start() exiting"); return previousState; } /** * Stops this server asynchronously.

* * This method returns immediately, regardless of current state. In order * to discover the success or failure of this operation, server state must * be polled or a subclass of Server must be used that overrides the * setState method to provide state change notification. * * @return the server state noted at entry to this method */ public int stop() { printWithThread("stop() entered"); int previousState = getState(); if (serverThread == null) { printWithThread("stop() serverThread is null; no action taken"); return previousState; } releaseServerSocket(); printWithThread("stop() exiting"); return previousState; } /** * Retrieves whether the specified socket should be allowed to make a * connection. * * @param socket the socket to test. * @return boolean */ protected boolean allowConnection(Socket socket) { if (isShuttingDown) { return false; } return (acl == null) ? true : acl.permitAccess( socket.getInetAddress().getAddress()); } /** * Initializes this server, setting the accepted connection protocol. * * @param protocol typically either SC_PROTOCOL_HTTP or SC_PROTOCOL_HSQL */ protected void init(int protocol) { // PRE: This method is only called from the constructor serverState = ServerConstants.SERVER_STATE_SHUTDOWN; serverConnSet = new HashSet(); serverId = toString(); serverId = serverId.substring(serverId.lastIndexOf('.') + 1); serverProtocol = protocol; serverProperties = ServerConfiguration.newDefaultProperties(protocol); logWriter = new PrintWriter(System.out); errWriter = new PrintWriter(System.err); setLogToSystem(isTrace()); } /** * Sets the server state value. * * @param state the new value */ protected synchronized void setState(int state) { serverState = state; } /** * This is called from org.hsqldb.DatabaseManager when a database is * shutdown. This shuts the server down if it is the last database * * @param id database id */ public final void notify(int id) { printWithThread("notify( database shutdown," + id + ") entered"); releaseDatabase(id); boolean shutdown = true; for (int i = 0; i < dbID.length; i++) { if (dbAlias[i] != null) { shutdown = false; break; } } if (!isRemoteOpen && shutdown) { stop(); } } /** * This releases the resources used for a database. Is called with id 0 * multiple times for non-existent databases * * @param id int */ final synchronized void releaseDatabase(int id) { ServerConnection[] array; printWithThread("releaseDatabase(" + id + ") entered"); // check all slots as a database may be opened by multiple aliases for (int i = 0; i < dbID.length; i++) { if (dbID[i] == id && dbAlias[i] != null) { dbID[i] = 0; dbActionSequence[i] = 0; dbAlias[i] = null; dbPath[i] = null; dbType[i] = null; dbProps[i] = null; } } synchronized (serverConnSetSync) { array = new ServerConnection[serverConnSet.size()]; serverConnSet.toArray(array); } for (int i = 0; i < array.length; i++) { ServerConnection sc = array[i]; if (sc.dbID == id) { sc.signalClose(); } } printWithThread("releaseDatabase(" + id + ") exiting"); } /** * Prints the specified message, s, formatted to identify that the print * operation is against this server instance. * * @param msg The message to print */ protected void print(String msg) { PrintWriter writer = logWriter; if (writer != null) { writer.println("[" + serverId + "]: " + msg); writer.flush(); } } /** * Prints value from server's resource bundle, formatted to * identify that the print operation is against this server instance. * Value may be localized according to the default JVM locale * * @param key the resource key */ final void printResource(String key) { String resource; StringTokenizer st; if (serverBundleHandle < 0) { return; } resource = ResourceBundleHandler.getString(serverBundleHandle, key); if (resource == null) { return; } st = new StringTokenizer(resource, "\n\r"); while (st.hasMoreTokens()) { print(st.nextToken()); } } /** * Prints the stack trace of the Throwable, t, to this Server object's * errWriter.

* * @param t the Throwable whose stack trace is to be printed */ protected void printStackTrace(Throwable t) { if (errWriter != null) { t.printStackTrace(errWriter); errWriter.flush(); } } /** * Prints the specified message, s, prepended with a timestamp representing * the current date and time, formatted to identify that the print * operation is against this server instance. * * @param msg the message to print */ final void printWithTimestamp(String msg) { print(sysTime.getTimestampString() + " " + msg); } /** * Prints a message formatted similarly to print(String), additionally * identifying the current (calling) thread. Replaces old method * trace(String msg). * * @param msg the message to print */ protected void printWithThread(String msg) { if (!isSilent()) { print("[" + Thread.currentThread() + "]: " + msg); } } /** * Prints an error message to this Server object's errWriter. * The message is formatted similarly to print(String), * additionally identifying the current (calling) thread. * * @param msg the message to print */ protected void printError(String msg) { PrintWriter writer = errWriter; if (writer != null) { writer.print("[" + serverId + "]: "); writer.print("[" + Thread.currentThread() + "]: "); writer.println(msg); writer.flush(); } } /** * Prints a description of the request encapsulated by the * Result argument, r. * * Printing occurs iff isSilent() is false.

* * The message is formatted similarly to print(String), additionally * indicating the connection identifier.

* * For Server instances, cid is typically the value assigned to each * ServerConnection object that is unique amongst all such identifiers * in each distinct JVM session / class loader * context.

* * For WebServer instances, a single logical connection actually spawns * a new physical WebServerConnection object for each request, so the * cid is typically the underlying session id, since that does not * change for the duration of the logical connection. * * @param cid the connection identifier * @param r the request whose description is to be printed */ final void printRequest(long cid, Result r) { if (isSilent()) { return; } StringBuilder sb = new StringBuilder(); sb.append(cid); sb.append(':'); switch (r.getType()) { case ResultConstants.PREPARE : { sb.append("SQLCLI:SQLPREPARE "); sb.append(r.getMainString()); break; } case ResultConstants.EXECDIRECT : { sb.append(r.getMainString()); break; } case ResultConstants.EXECUTE_INVALID : case ResultConstants.EXECUTE : { sb.append("SQLCLI:SQLEXECUTE:"); sb.append(r.getStatementID()); break; } case ResultConstants.BATCHEXECUTE : sb.append("SQLCLI:SQLEXECUTE:"); sb.append("BATCHMODE:"); sb.append(r.getStatementID()); break; case ResultConstants.UPDATE_RESULT : { sb.append("SQLCLI:RESULTUPDATE:"); sb.append(r.getStatementID()); break; } case ResultConstants.FREESTMT : { sb.append("SQLCLI:SQLFREESTMT:"); sb.append(r.getStatementID()); break; } case ResultConstants.GETSESSIONATTR : { sb.append("HSQLCLI:GETSESSIONATTR"); break; } case ResultConstants.SETSESSIONATTR : { sb.append("HSQLCLI:SETSESSIONATTR:"); break; } case ResultConstants.ENDTRAN : { sb.append("SQLCLI:SQLENDTRAN:"); switch (r.getActionType()) { case ResultConstants.TX_COMMIT : sb.append("COMMIT"); break; case ResultConstants.TX_ROLLBACK : sb.append("ROLLBACK"); break; case ResultConstants.TX_SAVEPOINT_NAME_RELEASE : sb.append("SAVEPOINT_NAME_RELEASE "); sb.append(r.getMainString()); break; case ResultConstants.TX_SAVEPOINT_NAME_ROLLBACK : sb.append("SAVEPOINT_NAME_ROLLBACK "); sb.append(r.getMainString()); break; default : sb.append(r.getActionType()); } break; } case ResultConstants.STARTTRAN : { sb.append("SQLCLI:SQLSTARTTRAN"); break; } case ResultConstants.DISCONNECT : { sb.append("SQLCLI:SQLDISCONNECT"); break; } case ResultConstants.SETCONNECTATTR : { sb.append("SQLCLI:SQLSETCONNECTATTR:"); switch (r.getConnectionAttrType()) { case ResultConstants.SQL_ATTR_SAVEPOINT_NAME : { sb.append("SQL_ATTR_SAVEPOINT_NAME "); sb.append(r.getMainString()); break; } default : { sb.append(r.getConnectionAttrType()); } } break; } case ResultConstants.CLOSE_RESULT : { sb.append("HQLCLI:CLOSE_RESULT:RESULT_ID "); sb.append(r.getResultId()); break; } case ResultConstants.REQUESTDATA : { sb.append("HQLCLI:REQUESTDATA:RESULT_ID "); sb.append(r.getResultId()); sb.append(" ROWOFFSET "); sb.append(r.getUpdateCount()); sb.append(" ROWCOUNT "); sb.append(r.getFetchSize()); break; } default : { sb.append("SQLCLI:MODE:"); sb.append(r.getType()); break; } } print(sb.toString()); } /** * return database ID * * @param aliasPath String * @return int */ synchronized final int getDBIndex(String aliasPath) { int semipos = aliasPath.indexOf(';'); String alias = aliasPath; String filepath = null; if (semipos != -1) { alias = aliasPath.substring(0, semipos); filepath = aliasPath.substring(semipos + 1); } int dbIndex = ArrayUtil.find(dbAlias, alias); if (dbIndex == -1) { if (filepath == null) { HsqlException e = Error.error(ErrorCode.GENERAL_ERROR, "database alias does not exist"); printError("database alias=" + alias + " does not exist"); setServerError(e); throw e; } else { return openDatabase(alias, filepath); } } else { return dbIndex; } } /** * Open and return database index * * @param alias String * @param datapath String * @return int */ final int openDatabase(String alias, String datapath) { if (!isRemoteOpen) { HsqlException e = Error.error(ErrorCode.GENERAL_ERROR, "remote open not allowed"); printError("Remote database open not allowed"); setServerError(e); throw e; } int i = getFirstEmptyDatabaseIndex(); if (i < -1) { i = closeOldestDatabase(); if (i < -1) { HsqlException e = Error.error(ErrorCode.GENERAL_ERROR, "limit of open databases reached"); printError("limit of open databases reached"); setServerError(e); throw e; } } HsqlProperties newprops = DatabaseURL.parseURL(datapath, false, false); if (newprops == null) { HsqlException e = Error.error(ErrorCode.GENERAL_ERROR, "invalid database path"); printError("invalid database path"); setServerError(e); throw e; } String path = newprops.getProperty(DatabaseURL.url_database); String type = newprops.getProperty(DatabaseURL.url_connection_type); try { int dbid = DatabaseManager.getDatabase(type, path, this, newprops); dbID[i] = dbid; dbActionSequence[i] = actionSequence; dbAlias[i] = alias; dbPath[i] = path; dbType[i] = type; dbProps[i] = newprops; return i; } catch (HsqlException e) { printError("Database [index=" + i + ", db=" + dbType[i] + dbPath[i] + ", alias=" + dbAlias[i] + "] did not open: " + e.toString()); setServerError(e); throw e; } } final int getFirstEmptyDatabaseIndex() { for (int i = 0; i < dbAlias.length; i++) { if (dbAlias[i] == null) { return i; } } return -1; } /** * Opens this server's database instances. This method returns true If at * least one database goes online, otherwise it returns false. If opening * any of the databases is attempted and an exception is thrown, the server * error is set to this exception. * * @return boolean */ final boolean openDatabases() { printWithThread("openDatabases() entered"); boolean success = false; setDBInfoArrays(); for (int i = 0; i < dbAlias.length; i++) { if (dbAlias[i] == null) { continue; } printWithThread("Opening database: [" + dbType[i] + dbPath[i] + "]"); StopWatch sw = new StopWatch(); int id; try { id = DatabaseManager.getDatabase(dbType[i], dbPath[i], this, dbProps[i]); dbID[i] = id; success = true; } catch (HsqlException e) { printError("Database [index=" + i + ", db=" + dbType[i] + dbPath[i] + ", alias=" + dbAlias[i] + "] did not open: " + e.toString()); setServerError(e); dbAlias[i] = null; dbPath[i] = null; dbType[i] = null; dbProps[i] = null; continue; } sw.stop(); String msg = "Database [index=" + i + ", id=" + id + ", db=" + dbType[i] + dbPath[i] + ", alias=" + dbAlias[i] + "] opened successfully"; print(sw.elapsedTimeToMessage(msg)); } printWithThread("openDatabases() exiting"); if (isRemoteOpen) { success = true; } if (!success && getServerError() == null) { // database alias / path list is empty or without full info for any DB setServerError(Error.error(ErrorCode.SERVER_NO_DATABASE)); } return success; } /** * Initialises the database attributes lists from the server properties object. */ private void setDBInfoArrays() { IntKeyHashMap dbNumberMap = getDBNameArray(); int maxDatabases = dbNumberMap.size(); if (serverProperties.isPropertyTrue( ServerProperties.sc_key_remote_open_db)) { int max = serverProperties.getIntegerProperty( ServerProperties.sc_key_max_databases, ServerConstants.SC_DEFAULT_MAX_DATABASES); if (maxDatabases < max) { maxDatabases = max; } } dbAlias = new String[maxDatabases]; dbPath = new String[dbAlias.length]; dbType = new String[dbAlias.length]; dbID = new int[dbAlias.length]; dbActionSequence = new long[dbAlias.length]; dbProps = new HsqlProperties[dbAlias.length]; Iterator it = dbNumberMap.keySet().iterator(); for (int i = 0; it.hasNext(); ) { int dbNumber = it.nextInt(); String path = getDatabasePath(dbNumber, true); if (path == null) { printWithThread("missing database path: " + dbNumberMap.get(dbNumber)); continue; } HsqlProperties dbURL = DatabaseURL.parseURL(path, false, false); if (dbURL == null) { printWithThread("malformed database path: " + path); continue; } dbAlias[i] = (String) dbNumberMap.get(dbNumber); dbPath[i] = dbURL.getProperty("database"); dbType[i] = dbURL.getProperty("connection_type"); dbProps[i] = dbURL; i++; } } /** * Returns a map of n values from server.dbname.n values to database names * from the properties object. * * @return IntKeyHashMap */ private IntKeyHashMap getDBNameArray() { final String prefix = ServerProperties.sc_key_dbname + "."; final int prefixLen = prefix.length(); IntKeyHashMap idToAliasMap = new IntKeyHashMap(); Enumeration en = serverProperties.propertyNames(); for (; en.hasMoreElements(); ) { String key = (String) en.nextElement(); if (!key.startsWith(prefix)) { continue; } int dbNumber; try { dbNumber = Integer.parseInt(key.substring(prefixLen)); } catch (NumberFormatException e1) { printWithThread("malformed database enumerator: " + key); continue; } String alias = serverProperties.getProperty(key).toLowerCase(); if (!aliasSet.add(alias)) { printWithThread("duplicate alias: " + alias); } Object existing = idToAliasMap.put(dbNumber, alias); if (existing != null) { printWithThread("duplicate database enumerator: " + key); } } return idToAliasMap; } /** * Constructs and installs a new ServerSocket instance for this server. * * @throws Exception if it is not possible to construct and install * a new ServerSocket */ private void openServerSocket() throws Exception { String address; int port; String[] candidateAddrs; String emsg; StopWatch sw; printWithThread("openServerSocket() entered"); if (isTls()) { printWithThread("Requesting TLS/SSL-encrypted JDBC"); } sw = new StopWatch(); socketFactory = HsqlSocketFactory.getInstance(isTls()); address = getAddress(); port = getPort(); if (StringUtil.isEmpty(address) || ServerConstants.SC_DEFAULT_ADDRESS.equalsIgnoreCase( address.trim())) { socket = socketFactory.createServerSocket(port); } else { try { socket = socketFactory.createServerSocket(port, address); } catch (UnknownHostException e) { candidateAddrs = ServerConfiguration.listLocalInetAddressNames(); int messageID; String[] messageParameters; if (candidateAddrs.length > 0) { messageID = ErrorCode.M_SERVER_OPEN_SERVER_SOCKET_1; StringBuilder sb = new StringBuilder(); for (int i = 0; i < candidateAddrs.length; i++) { if (sb.length() > 0) { sb.append(", "); } sb.append(candidateAddrs[i]); } messageParameters = new String[] { address, sb.toString() }; } else { messageID = ErrorCode.M_SERVER_OPEN_SERVER_SOCKET_2; messageParameters = new String[]{ address }; } throw new UnknownHostException(Error.getMessage(messageID, 0, messageParameters)); } } /* * Following line necessary for Java 1.3 on UNIX. See accept() * comment elsewhere in this file. */ socket.setSoTimeout(1000); printWithThread("Got server socket: " + socket); print(sw.elapsedTimeToMessage("Server socket opened successfully")); if (socketFactory.isSecure()) { print("Using TLS/SSL-encrypted JDBC"); } printWithThread("openServerSocket() exiting"); } /** Prints a timestamped message indicating that this server is online */ private void printServerOnlineMessage() { String s = getProductName() + " " + getProductVersion() + " is online on port " + this.getPort(); printWithTimestamp(s); printResource("online.help"); } /** * Prints a description of the server properties iff !isSilent(). */ protected void printProperties() { Enumeration e; String key; String value; // Avoid the waste of generating each description, // only for trace() to silently discard it if (isSilent()) { return; } e = serverProperties.propertyNames(); while (e.hasMoreElements()) { key = (String) e.nextElement(); value = serverProperties.getProperty(key); printWithThread(key + "=" + value); } } /** * Puts this server into the SERVER_CLOSING state, closes the ServerSocket * and nullifies the reference to it. If the ServerSocket is already null, * this method exists immediately, otherwise, the result is to fully * shut down the server. */ private synchronized void releaseServerSocket() { printWithThread("releaseServerSocket() entered"); if (socket != null) { printWithThread("Releasing server socket: [" + socket + "]"); setState(ServerConstants.SERVER_STATE_CLOSING); try { socket.close(); } catch (IOException e) { printError("Exception closing server socket"); printError("releaseServerSocket(): " + e); } socket = null; } printWithThread("releaseServerSocket() exited"); } /** * Attempts to bring this server fully online by opening * a new ServerSocket, obtaining the hosted databases, * notifying the status waiter thread (if any) and * finally entering the listen loop if all else succeeds. * If any part of the process fails, then this server enters * its shutdown sequence. */ private void run() { StopWatch sw; String tgName; printWithThread("run() entered"); print("Initiating startup sequence..."); printProperties(); sw = new StopWatch(); setServerError(null); try { // Faster init first: // It is huge waste to fully open the databases, only // to find that the socket address is already in use openServerSocket(); } catch (Exception e) { setServerError(e); printError("run()/openServerSocket(): "); printStackTrace(e); shutdown(true); return; } tgName = "HSQLDB Connections @" + Integer.toString(this.hashCode(), 16); serverConnectionThreadGroup = new ThreadGroup(tgName); // Mount the databases this server is supposed to host. // This may take some time if the databases are not all // already open. if (!openDatabases()) { setServerError(null); printError("Shutting down because there are no open databases"); shutdown(true); return; } // At this point, we have a valid server socket and // a valid hosted database set, so its OK to start // listening for connections. setState(ServerConstants.SERVER_STATE_ONLINE); print(sw.elapsedTimeToMessage("Startup sequence completed")); printServerOnlineMessage(); // isShuttingDown is only read after socket connections are 'accept'ed. isShuttingDown = false; // In case shutdown was aborted previously. try { /* * This loop is necessary for UNIX w/ Sun Java 1.3 because * in that case the socket.close() elsewhere will not * interrupt this accept(). */ while (socket != null) { try { handleConnection(socket.accept()); } catch (java.io.InterruptedIOException iioe) {} } } catch (IOException ioe) { if (getState() == ServerConstants.SERVER_STATE_ONLINE) { setServerError(ioe); printError(this + ".run()/handleConnection(): "); printStackTrace(ioe); } } catch (Throwable t) { printWithThread(t.toString()); } finally { shutdown(false); // or maybe getServerError() != null? } } /** * Sets this Server's last encountered error state. * * @param t The new value for the server error */ protected void setServerError(Throwable t) { serverError = t; } /** * Shuts down all the database served by this server. As a consequence, * this server and any other server that is serving a subset of the * databases will be shutdown, unless the server was started with * server.remote_open property. * * The shutdownMode must be one of: * *

    *
  • org.hsqldb.Database.CLOSEMODE_IMMEDIATELY = 1 *
  • org.hsqldb.Database.CLOSEMODE_NORMAL = 2 *
  • org.hsqldb.Database.CLOSEMODE_COMPACT = 3 *
  • org.hsqldb.Database.CLOSEMODE_SCRIPT = 4 *
* @param shutdownMode a value between 1-4, usually 2 for NORMAL. */ public void shutdownCatalogs(int shutdownMode) { if (shutdownMode < Database.CLOSEMODE_IMMEDIATELY || shutdownMode > Database.CLOSEMODE_SCRIPT) { throw new IllegalArgumentException("must be between 1 and 4"); } DatabaseManager.shutdownDatabases(this, shutdownMode); } /** * Shuts down this server and all the database served by this server. As a * consequence, any other server that is serving a subset of the databases * will be shutdown, unless the server was started with server.remote_open * property. * * The shutdownMode must be one of: * *
    *
  • org.hsqldb.Database.CLOSEMODE_IMMEDIATELY = 1 *
  • org.hsqldb.Database.CLOSEMODE_NORMAL = 2 *
  • org.hsqldb.Database.CLOSEMODE_COMPACT = 3 *
  • org.hsqldb.Database.CLOSEMODE_SCRIPT = 4 *
* @param shutdownMode a value between 1-4, usually 2 for NORMAL. */ public void shutdownWithCatalogs(int shutdownMode) { if (shutdownMode < Database.CLOSEMODE_IMMEDIATELY || shutdownMode > Database.CLOSEMODE_SCRIPT) { throw new IllegalArgumentException("must be between 1 and 4"); } // If an unchecked exception is thrown, isShuttingDown will be left true, // which is good from a security standpoint. isShuttingDown = true; // make handleConnection() reject new connection attempts DatabaseManager.shutdownDatabases(this, shutdownMode); shutdown(false); isShuttingDown = false; } /** * External method to shut down this server. */ public void shutdown() { shutdown(false); } /** * Shuts down this server. * * @param error true if shutdown is in response to an error * state, else false */ protected synchronized void shutdown(boolean error) { if (serverState == ServerConstants.SERVER_STATE_SHUTDOWN) { return; } printWithThread("shutdown() entered"); print("Initiating shutdown sequence..."); StopWatch sw = new StopWatch(); releaseServerSocket(); DatabaseManager.deRegisterServer(this); if (dbPath != null) { for (int i = 0; i < dbPath.length; i++) { releaseDatabase(dbID[i]); } } // Be nice and let applications exit if there are no // running connection threads - wait at most 100 ms per active thread if (serverConnectionThreadGroup != null) { int count = serverConnectionThreadGroup.activeCount(); for (; count > 0 && serverConnectionThreadGroup.activeCount() > 0; count--) { try { Thread.sleep(100); } catch (Exception e) { // e.getMessage(); } } serverConnectionThreadGroup = null; } serverThread = null; setState(ServerConstants.SERVER_STATE_SHUTDOWN); print(sw.elapsedTimeToMessage("Shutdown sequence completed")); if (isNoSystemExit()) { printWithTimestamp("SHUTDOWN : System.exit() was not called"); printWithThread("shutdown() exited"); } else { printWithTimestamp("SHUTDOWN : System.exit() is called next"); printWithThread("shutdown() exiting..."); try { System.exit(0); } catch (Throwable t) { printWithThread(t.toString()); } } } /** * Used by Connection object * * @param dbIndex int */ synchronized void setActionSequence(int dbIndex) { dbActionSequence[dbIndex] = actionSequence++; } /** * Feature is turned off by, pending a property to allow it. * * @return int */ protected int closeOldestDatabase() { return -1; /* int index = -1; Database db; synchronized (this) { long min = Long.MAX_VALUE; for (int i = 0; i < dbActionSequence.length; i++) { if (min > dbActionSequence[i]) { min = dbActionSequence[i]; index = i; } } if (index == -1) { return -1; } db = DatabaseManager.lookupDatabaseObject(dbType[index], dbPath[index]); if (db == null) { return -1; } } db.close(Database.CLOSEMODE_IMMEDIATELY); return index; */ } /** * Prints message for the specified key, without any special * formatting. The message content comes from the server * resource bundle and thus may localized according to the default * JVM locale.

* * Uses System.out directly instead of Trace.printSystemOut() so it * always prints, regardless of Trace settings. * * @param key for message */ protected static void printHelp(String key) { System.out.println(ResourceBundleHandler.getString(serverBundleHandle, key)); } /** * Creates and starts a new Server.

* * Allows starting a Server via the command line interface.

* * @param args the command line arguments for the Server instance */ public static void main(String[] args) { HsqlProperties argProps; argProps = HsqlProperties.argArrayToProps(args, ServerProperties.sc_key_prefix); String[] errors = argProps.getErrorKeys(); if (errors.length != 0) { System.out.println("no value for argument:" + errors[0]); printHelp("server.help"); return; } String propsPath = argProps.getProperty(ServerProperties.sc_key_props); String propsExtension = ""; if (propsPath == null) { propsPath = "server"; propsExtension = ".properties"; } else { argProps.removeProperty(ServerProperties.sc_key_props); } propsPath = FileUtil.getFileUtil().canonicalOrAbsolutePath(propsPath); ServerProperties fileProps = ServerConfiguration.getPropertiesFromFile( ServerConstants.SC_PROTOCOL_HSQL, propsPath, propsExtension); ServerProperties props = fileProps == null ? new ServerProperties(ServerConstants.SC_PROTOCOL_HSQL) : fileProps; props.addProperties(argProps); ServerConfiguration.translateDefaultDatabaseProperty(props); // Standard behaviour when started from the command line // is to halt the VM when the server shuts down. This may, of // course, be overridden by whatever, if any, security policy // is in place. ServerConfiguration.translateDefaultNoSystemExitProperty(props); ServerConfiguration.translateAddressProperty(props); // finished setting up properties; Server server = new Server(); try { server.setProperties(props); } catch (Exception e) { server.printError("Failed to set properties"); server.printStackTrace(e); return; } // now messages go to the channel specified in properties server.print("Startup sequence initiated from main() method"); if (fileProps != null) { server.print("Loaded properties from [" + propsPath + propsExtension + "]"); } else { server.print("Could not load properties from file"); server.print("Using cli/default properties only"); } server.start(); } private static void setLogToSystem(boolean value) { try { PrintWriter newPrintWriter = (value) ? new PrintWriter(System.out) : null; DriverManager.setLogWriter(newPrintWriter); } catch (Exception e) {} } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy