KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > Server


1 /* Copyright (c) 2001-2005, The HSQL Development Group
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the HSQL Development Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

30
31
32 package org.hsqldb;
33
34 import java.io.File JavaDoc;
35 import java.io.IOException JavaDoc;
36 import java.io.PrintWriter JavaDoc;
37 import java.net.ServerSocket JavaDoc;
38 import java.net.Socket JavaDoc;
39 import java.net.UnknownHostException JavaDoc;
40 import java.util.Enumeration JavaDoc;
41 import java.util.StringTokenizer JavaDoc;
42
43 import org.hsqldb.lib.ArrayUtil;
44 import org.hsqldb.lib.FileUtil;
45 import org.hsqldb.lib.HashSet;
46 import org.hsqldb.lib.Iterator;
47 import org.hsqldb.lib.StopWatch;
48 import org.hsqldb.lib.StringUtil;
49 import org.hsqldb.lib.WrapperIterator;
50 import org.hsqldb.lib.java.JavaSystem;
51 import org.hsqldb.persist.HsqlDatabaseProperties;
52 import org.hsqldb.persist.HsqlProperties;
53 import org.hsqldb.resources.BundleHandler;
54
55 // fredt@users 20020215 - patch 1.7.0
56
// methods reorganised to use new HsqlProperties class
57
// fredt@users 20020424 - patch 1.7.0 - shutdown without exit
58
// see the comments in ServerConnection.java
59
// unsaved@users 20021113 - patch 1.7.2 - SSL support
60
// boucherb@users 20030510-14 - 1.7.2 - SSL support moved to factory interface
61
// boucherb@users 20030510-14 - 1.7.2 - service control, JavaBean API
62
// fredt@users 20030916 - 1.7.2 - review, simplification and multiple DB's
63
// fredt@users 20040320 - 1.7.2 - review and correction
64
// fredt@users 20050225 - 1.8.0 - minor corrections
65
// fredt@users 20051231 - 1.8.1 - support for remote opening of databases
66

67 /**
68  * The HSQLDB HSQL protocol network database server. <p>
69  *
70  * A Server object acts as a network database server and is one way of using
71  * the client-server mode of HSQLDB Database Engine. Instances of this
72  * class handle native HSQL protocol connections exclusively, allowing database
73  * queries to be performed efficienly across the network. Server's direct
74  * descendent, WebServer, handles HTTP protocol connections exclusively,
75  * allowing HSQL protocol to be tunneled over HTTP to avoid sandbox and
76  * firewall issues, albeit less efficiently. <p>
77  *
78  * There are a number of ways to configure and start a Server instance. <p>
79  *
80  * When started from the command line or programatically via the main(String[])
81  * method, configuration occurs in three phases, with later phases overriding
82  * properties set by previous phases:
83  *
84  * <ol>
85  * <li>Upon construction, a Server object is assigned a set of default
86  * properties. <p>
87  *
88  * <li>If it exists, properties are loaded from a file named
89  * 'server.properties' in the present working directory. <p>
90  *
91  * <li>The command line arguments (alternatively, the String[] passed to
92  * main()) are parsed and used to further configure the Server's
93  * properties. <p>
94  *
95  * </ol> <p>
96  *
97  * From the command line, the options are as follows: <p>
98  * <pre>
99  * +----------------+-------------+----------+------------------------------+
100  * | OPTION | TYPE | DEFAULT | DESCRIPTION |
101  * +----------------+-------------+----------+------------------------------|
102  * | -? | -- | -- | prints this message |
103  * | -address | name|number | any | server inet address |
104  * | -port | number | 9001/544 | port at which server listens |
105  * | -database.i | [type]spec | 0=test | path of database i |
106  * | -dbname.i | alias | -- | url alias for database i |
107  * | -silent | true|false | true | false => display all queries |
108  * | -trace | true|false | false | display JDBC trace messages |
109  * | -tls | true|false | false | TLS/SSL (secure) sockets |
110  * | -no_system_exit| true|false | false | do not issue System.exit() |
111  * | -remote_open | true|false | false | can open databases remotely |
112  * +----------------+-------------+----------+------------------------------+
113  * </pre>
114  *
115  * The <em>database.i</em> and <em>dbname.i</em> options need further
116  * explanation:
117  *
118  * <ul>
119  * <li>Multiple databases can be served by each instance of the Server.
120  * The value of <em>i</em> is currently limited to the range 0..9,
121  * allowing up to 10 different databases. Any number is this range
122  * can be used.<p>
123  *
124  * <li>The value assigned to <em>database.i</em> is interpreted using the
125  * format <b>'[type]spec'</b>, where the optional <em>type</em> component
126  * is one of <b>'file:'</b>, <b>'res:'</b> or <b>'mem:'</b> and the
127  * <em>spec</em> component is interpreted in the context of the
128  * <em>type</em> component. <p>
129  *
130  * If omitted, the <em>type</em> component is taken to be
131  * <b>'file:'</b>. <p>
132  *
133  * A full description of how
134  * <b>'[type]spec'</b> values are interpreted appears in the overview for
135  * {@link org.hsqldb.jdbc.jdbcConnection jdbcConnection}. <p>
136  *
137  * <li>The value assigned to <em>dbname.i</em> is taken to be the key used to
138  * look up the desired database instance and thus corresponds to the
139  * <b>&lt;alias&gt;</b> component of the HSQLDB HSQL protocol database
140  * connection url:
141  * 'jdbc:hsqldb:hsql[s]://host[port][/<b>&lt;alias&gt;</b>]'. <p>
142  *
143  * <li>The value of <em>database.0</em> is special. If <em>dbname.0</em>
144  * is not specified, then this defaults to an empty string and
145  * a connection is made to <em>database.0</em> path when
146  * the <b>&lt;alias&gt;</b> component of an HSQLDB HSQL protocol database
147  * connection url is omitted. If a <em>database</em> key/value pair is
148  * found in the properties when the main method is called, this
149  * pair is supersedes the <em>database.0</em> setting<p>
150  *
151  * This behaviour allows the previous
152  * database connection url format to work with essentially unchanged
153  * semantics.<p>
154  * </ul>
155  *
156  * From the 'server.properties' file, options can be set similarly, using a
157  * slightly different format. <p>
158  *
159  * Here is an example 'server.properties' file:
160  *
161  * <pre>
162  * server.port=9001
163  * server.database.0=test
164  * server.dbname.0=...
165  * ...
166  * server.database.n=...
167  * server.dbname.n=...
168  * server.silent=true
169  * </pre>
170  *
171  * Starting with 1.7.2, Server has been refactored to become a simple JavaBean
172  * with non-blocking start() and stop() service methods. It is possible to
173  * configure a Server instance through the JavaBean API as well, but this
174  * part of the public interface is still under review and will not be finalized
175  * or documented fully until the final 1.7.2 release. <p>
176  *
177  * <b>Note:</b> <p>
178  *
179  * The 'no_system_exit' property is of particular interest. <p>
180  *
181  * If a Server instance is to run embedded in, say, an application server,
182  * such as when the jdbcDataSource or HsqlServerFactory classes are used, it
183  * is typically necessary to avoid calling System.exit() when the Server
184  * instance shuts down. <p>
185  *
186  * By default, 'no_system_exit' is set: <p>
187  *
188  * <ol>
189  * <li><b>true</b> when a Server is started directly from the start()
190  * method. <p>
191  *
192  * <li><b>false</b> when a Server is started from the main(String[])
193  * method.
194  * </ol> <p>
195  *
196  * These values are natural to their context because the first case allows
197  * the JVM to exit by default on Server shutdown when a Server instance is
198  * started from a command line environment, whereas the second case prevents
199  * a typically unwanted JVM exit on Server shutdown when a Server intance
200  * is started as part of a larger framework. <p>
201  *
202  * Replaces original Hypersonic source of the same name.
203  *
204  * @author fredt@users
205  * @version 1.8.0
206  * @since 1.7.2
207  *
208  * @jmx.mbean
209  * description="HSQLDB Server"
210  * extends="org.hsqldb.mx.mbean.RegistrationSupportBaseMBean"
211  *
212  * @jboss.xmbean
213  */

214 public class Server implements HsqlSocketRequestHandler {
215
216 //
217
protected static final int serverBundleHandle =
218         BundleHandler.getBundleHandle("org_hsqldb_Server_messages", null);
219
220 //
221
HsqlProperties serverProperties;
222
223 //
224
HashSet serverConnSet;
225
226 //
227
private String JavaDoc[] dbAlias;
228     private String JavaDoc[] dbType;
229     private String JavaDoc[] dbPath;
230     private HsqlProperties[] dbProps;
231     private int[] dbID;
232
233 // Currently unused
234
private int maxConnections;
235
236 //
237
protected String JavaDoc serverId;
238     protected int serverProtocol;
239     protected ThreadGroup JavaDoc serverConnectionThreadGroup;
240     protected HsqlSocketFactory socketFactory;
241     protected ServerSocket JavaDoc socket;
242
243 //
244
private Thread JavaDoc serverThread;
245     private Throwable JavaDoc serverError;
246     private volatile int serverState;
247     private volatile boolean isSilent;
248     private volatile boolean isRemoteOpen;
249     private PrintWriter JavaDoc logWriter;
250     private PrintWriter JavaDoc errWriter;
251
252 //
253

254     /**
255      * A specialized Thread inner class in which the run() method of this
256      * server executes.
257      */

258     private class ServerThread extends Thread JavaDoc {
259
260         /**
261          * Constructs a new thread in which to execute the run method
262          * of this server.
263          *
264          * @param name The thread name
265          */

266         ServerThread(String JavaDoc name) {
267
268             super(name);
269
270             setName(name + '@'
271                     + Integer.toString(Server.this.hashCode(), 16));
272         }
273
274         /**
275          * Executes the run() method of this server
276          */

277         public void run() {
278             Server.this.run();
279             printWithThread("ServerThread.run() exited");
280         }
281     }
282
283     /**
284      * Creates a new Server instance handling HSQL protocol connections.
285      */

286     public Server() {
287         this(ServerConstants.SC_PROTOCOL_HSQL);
288     }
289
290     /**
291      * Creates a new Server instance handling the specified connection
292      * protocol. <p>
293      *
294      * For example, the no-args WebServer constructor invokes this constructor
295      * with ServerConstants.SC_PROTOCOL_HTTP, while the Server() no args
296      * contructor invokes this constructor with
297      * ServerConstants.SC_PROTOCOL_HSQL. <p>
298      *
299      * @param protocol the ServerConstants code indicating which
300      * connection protocol to handle
301      */

302     protected Server(int protocol) {
303         init(protocol);
304     }
305
306     /**
307      * Creates and starts a new Server. <p>
308      *
309      * Allows starting a Server via the command line interface. <p>
310      *
311      * @param args the command line arguments for the Server instance
312      */

313     public static void main(String JavaDoc[] args) {
314
315         String JavaDoc propsPath = FileUtil.canonicalOrAbsolutePath("server");
316         HsqlProperties fileProps =
317             ServerConfiguration.getPropertiesFromFile(propsPath);
318         HsqlProperties props = fileProps == null ? new HsqlProperties()
319                                                  : fileProps;
320         HsqlProperties stringProps = HsqlProperties.argArrayToProps(args,
321             ServerConstants.SC_KEY_PREFIX);
322
323         if (stringProps != null) {
324             if (stringProps.getErrorKeys().length != 0) {
325                 printHelp("server.help");
326
327                 return;
328             }
329
330             props.addProperties(stringProps);
331         }
332
333         ServerConfiguration.translateDefaultDatabaseProperty(props);
334
335         // Standard behaviour when started from the command line
336
// is to halt the VM when the server shuts down. This may, of
337
// course, be overridden by whatever, if any, security policy
338
// is in place.
339
ServerConfiguration.translateDefaultNoSystemExitProperty(props);
340
341         // finished setting up properties;
342
Server server = new Server();
343
344         server.setProperties(props);
345
346         // now messages go to the channel specified in properties
347
server.print("Startup sequence initiated from main() method");
348
349         if (fileProps != null) {
350             server.print("Loaded properties from [" + propsPath
351                          + ".properties]");
352         } else {
353             server.print("Could not load properties from file");
354             server.print("Using cli/default properties only");
355         }
356
357         server.start();
358     }
359
360     /**
361      * Checks if this Server object is or is not running and throws if the
362      * current state does not match the specified value.
363      *
364      * @param running if true, ensure the server is running, else ensure the
365      * server is not running
366      * @throws RuntimeException if the supplied value does not match the
367      * current running status
368      */

369     public void checkRunning(boolean running) throws RuntimeException JavaDoc {
370
371         int state;
372         boolean error;
373
374         printWithThread("checkRunning(" + running + ") entered");
375
376         state = getState();
377         error = (running && state != ServerConstants.SERVER_STATE_ONLINE)
378                 || (!running
379                     && state != ServerConstants.SERVER_STATE_SHUTDOWN);
380
381         if (error) {
382             String JavaDoc msg = "server is " + (running ? "not "
383                                                  : "") + "running";
384
385             throw new RuntimeException JavaDoc(msg);
386         }
387
388         printWithThread("checkRunning(" + running + ") exited");
389     }
390
391     /**
392      * Closes all connections to this Server.
393      *
394      * @jmx.managed-operation
395      * impact="ACTION"
396      * description="Closes all open connections"
397      */

398     public synchronized void signalCloseAllServerConnections() {
399
400         Iterator it;
401
402         printWithThread("signalCloseAllServerConnections() entered");
403
404         synchronized (serverConnSet) {
405
406             // snapshot
407
it = new WrapperIterator(serverConnSet.toArray(null));
408         }
409
410         for (; it.hasNext(); ) {
411             ServerConnection sc = (ServerConnection) it.next();
412
413             printWithThread("Closing " + sc);
414
415             // also removes all but one connection from serverConnSet
416
sc.signalClose();
417         }
418
419         printWithThread("signalCloseAllServerConnections() exited");
420     }
421
422     protected void finalize() throws Throwable JavaDoc {
423
424         if (serverThread != null) {
425             releaseServerSocket();
426         }
427     }
428
429     /**
430      * Retrieves, in string form, this server's host address.
431      *
432      * @return this server's host address
433      *
434      * @jmx.managed-attribute
435      * access="read-write"
436      * description="Host InetAddress"
437      */

438     public String JavaDoc getAddress() {
439
440         return socket == null
441                ? serverProperties.getProperty(ServerConstants.SC_KEY_ADDRESS)
442                : socket.getInetAddress().getHostAddress();
443     }
444
445     /**
446      * Retrieves the url alias (network name) of the i'th database
447      * that this Server hosts.
448      *
449      * @param index the index of the url alias upon which to report
450      * @param asconfigured if true, report the configured value, else
451      * the live value
452      * @return the url alias component of the i'th database
453      * that this Server hosts, or null if no such name exists.
454      *
455      * @jmx.managed-operation
456      * impact="INFO"
457      * description="url alias component of the i'th hosted Database"
458      *
459      * @jmx.managed-operation-parameter
460      * name="index"
461      * type="int"
462      * position="0"
463      * description="This Server's index for the hosted Database"
464      *
465      * @jmx.managed-operation-parameter
466      * name="asconfigured"
467      * type="boolean"
468      * position="1"
469      * description="if true, the configured value, else the live value"
470      */

471     public String JavaDoc getDatabaseName(int index, boolean asconfigured) {
472
473         if (asconfigured) {
474             return serverProperties.getProperty(ServerConstants.SC_KEY_DBNAME
475                                                 + "." + index);
476         } else if (getState() == ServerConstants.SERVER_STATE_ONLINE) {
477             return (dbAlias == null || index < 0 || index >= dbAlias.length)
478                    ? null
479                    : dbAlias[index];
480         } else {
481             return null;
482         }
483     }
484
485     /**
486      * Retrieves the HSQLDB path descriptor (uri) of the i'th
487      * Database that this Server hosts.
488      *
489      * @param index the index of the uri upon which to report
490      * @param asconfigured if true, report the configured value, else
491      * the live value
492      * @return the HSQLDB database path descriptor of the i'th database
493      * that this Server hosts, or null if no such path descriptor
494      * exists
495      *
496      * @jmx.managed-operation
497      * impact="INFO"
498      * description="For i'th hosted database"
499      *
500      * @jmx.managed-operation-parameter
501      * name="index"
502      * type="int"
503      * position="0"
504      * description="This Server's index for the hosted Database"
505      *
506      * @jmx.managed-operation-parameter
507      * name="asconfigured"
508      * type="boolean"
509      * position="1"
510      * description="if true, the configured value, else the live value"
511      */

512     public String JavaDoc getDatabasePath(int index, boolean asconfigured) {
513
514         if (asconfigured) {
515             return serverProperties.getProperty(
516                 ServerConstants.SC_KEY_DATABASE + "." + index);
517         } else if (getState() == ServerConstants.SERVER_STATE_ONLINE) {
518             return (dbPath == null || index < 0 || index >= dbPath.length)
519                    ? null
520                    : dbPath[index];
521         } else {
522             return null;
523         }
524     }
525
526     public String JavaDoc getDatabaseType(int index) {
527         return (dbType == null || index < 0 || index >= dbType.length) ? null
528                                                                        : dbType[index];
529     }
530
531     /**
532      * Retrieves the name of the web page served when no page is specified.
533      * This attribute is relevant only when server protocol is HTTP(S).
534      *
535      * @return the name of the web page served when no page is specified
536      *
537      * @jmx.managed-attribute
538      * access="read-write"
539      * description="Used when server protocol is HTTP(S)"
540      */

541     public String JavaDoc getDefaultWebPage() {
542         return "[IGNORED]";
543     }
544
545     /**
546      * Retrieves a String object describing the command line and
547      * properties options for this Server.
548      *
549      * @return the command line and properties options help for this Server
550      */

551     public String JavaDoc getHelpString() {
552         return BundleHandler.getString(serverBundleHandle, "server.help");
553     }
554
555     /**
556      * Retrieves the PrintWriter to which server errors are printed.
557      *
558      * @return the PrintWriter to which server errors are printed.
559      */

560     public PrintWriter JavaDoc getErrWriter() {
561         return errWriter;
562     }
563
564     /**
565      * Retrieves the PrintWriter to which server messages are printed.
566      *
567      * @return the PrintWriter to which server messages are printed.
568      */

569     public PrintWriter JavaDoc getLogWriter() {
570         return logWriter;
571     }
572
573     /**
574      * Retrieves this server's host port.
575      *
576      * @return this server's host port
577      *
578      * @jmx.managed-attribute
579      * access="read-write"
580      * description="At which ServerSocket listens for connections"
581      */

582     public int getPort() {
583
584         return serverProperties.getIntegerProperty(
585             ServerConstants.SC_KEY_PORT,
586             ServerConfiguration.getDefaultPort(serverProtocol, isTls()));
587     }
588
589     /**
590      * Retrieves this server's product name. <p>
591      *
592      * Typically, this will be something like: "HSQLDB xxx server".
593      *
594      * @return the product name of this server
595      *
596      * @jmx.managed-attribute
597      * access="read-only"
598      * description="Of Server"
599      */

600     public String JavaDoc getProductName() {
601         return "HSQLDB server";
602     }
603
604     /**
605      * Retrieves the server's product version, as a String. <p>
606      *
607      * Typically, this will be something like: "1.x.x" or "2.x.x" and so on.
608      *
609      * @return the product version of the server
610      *
611      * @jmx.managed-attribute
612      * access="read-only"
613      * description="Of Server"
614      */

615     public String JavaDoc getProductVersion() {
616         return HsqlDatabaseProperties.THIS_VERSION;
617     }
618
619     /**
620      * Retrieves a string respresentaion of the network protocol
621      * this server offers, typically one of 'HTTP', HTTPS', 'HSQL' or 'HSQLS'.
622      *
623      * @return string respresentation of this server's protocol
624      *
625      * @jmx.managed-attribute
626      * access="read-only"
627      * description="Used to handle connections"
628      */

629     public String JavaDoc getProtocol() {
630         return isTls() ? "HSQLS"
631                        : "HSQL";
632     }
633
634     /**
635      * Retrieves a Throwable indicating the last server error, if any. <p>
636      *
637      * @return a Throwable indicating the last server error
638      *
639      * @jmx.managed-attribute
640      * access="read-only"
641      * description="Indicating last exception state"
642      */

643     public Throwable JavaDoc getServerError() {
644         return serverError;
645     }
646
647     /**
648      * Retrieves a String identifying this Server object.
649      *
650      * @return a String identifying this Server object
651      *
652      * @jmx.managed-attribute
653      * access="read-only"
654      * description="Identifying Server"
655      */

656     public String JavaDoc getServerId() {
657         return serverId;
658     }
659
660     /**
661      * Retrieves current state of this server in numerically coded form. <p>
662      *
663      * Typically, this will be one of: <p>
664      *
665      * <ol>
666      * <li>ServerProperties.SERVER_STATE_ONLINE (1)
667      * <li>ServerProperties.SERVER_STATE_OPENING (4)
668      * <li>ServerProperties.SERVER_STATE_CLOSING (8)
669      * <li>ServerProperties.SERVER_STATE_SHUTDOWN (16)
670      * </ol>
671      *
672      * @return this server's state code.
673      *
674      * @jmx.managed-attribute
675      * access="read-only"
676      * description="1:ONLINE 4:OPENING 8:CLOSING, 16:SHUTDOWN"
677      */

678     public synchronized int getState() {
679         return serverState;
680     }
681
682     /**
683      * Retrieves a character sequence describing this server's current state,
684      * including the message of the last exception, if there is one and it
685      * is still in context.
686      *
687      * @return this server's state represented as a character sequence.
688      *
689      * @jmx.managed-attribute
690      * access="read-only"
691      * description="State as string"
692      */

693     public String JavaDoc getStateDescriptor() {
694
695         String JavaDoc state;
696         Throwable JavaDoc t = getServerError();
697
698         switch (serverState) {
699
700             case ServerConstants.SERVER_STATE_SHUTDOWN :
701                 state = "SHUTDOWN";
702                 break;
703
704             case ServerConstants.SERVER_STATE_OPENING :
705                 state = "OPENING";
706                 break;
707
708             case ServerConstants.SERVER_STATE_CLOSING :
709                 state = "CLOSING";
710                 break;
711
712             case ServerConstants.SERVER_STATE_ONLINE :
713                 state = "ONLINE";
714                 break;
715
716             default :
717                 state = "UNKNOWN";
718                 break;
719         }
720
721         return state;
722     }
723
724     /**
725      * Retrieves the root context (directory) from which web content
726      * is served. This property is relevant only when the server
727      * protocol is HTTP(S). Although unlikely, it may be that in the future
728      * other contexts, such as jar urls may be supported, so that pages can
729      * be served from the contents of a jar or from the JVM class path.
730      *
731      * @return the root context (directory) from which web content is served
732      *
733      * @jmx.managed-attribute
734      * access="read-write"
735      * description="Context (directory)"
736      */

737     public String JavaDoc getWebRoot() {
738         return "[IGNORED]";
739     }
740
741     /**
742      * Assigns the specified socket to a new conection handler and
743      * starts the handler in a new Thread.
744      *
745      * @param s the socket to connect
746      */

747     public void handleConnection(Socket JavaDoc s) {
748
749         Thread JavaDoc t;
750         Runnable JavaDoc r;
751         String JavaDoc ctn;
752
753         printWithThread("handleConnection(" + s + ") entered");
754
755         if (!allowConnection(s)) {
756             try {
757                 s.close();
758             } catch (Exception JavaDoc e) {}
759
760             printWithThread("allowConnection(): connection refused");
761             printWithThread("handleConnection() exited");
762
763             return;
764         }
765
766         // Maybe set up socket options, SSL
767
// Session tracing/callbacks, etc.
768
if (socketFactory != null) {
769             socketFactory.configureSocket(s);
770         }
771
772         if (serverProtocol == ServerConstants.SC_PROTOCOL_HSQL) {
773             r = new ServerConnection(s, this);
774             ctn = ((ServerConnection) r).getConnectionThreadName();
775
776             synchronized (serverConnSet) {
777                 serverConnSet.add(r);
778             }
779         } else {
780             r = new WebServerConnection(s, (WebServer) this);
781             ctn = ((WebServerConnection) r).getConnectionThreadName();
782         }
783
784         t = new Thread JavaDoc(serverConnectionThreadGroup, r, ctn);
785
786         t.start();
787         printWithThread("handleConnection() exited");
788     }
789
790     /**
791      * Retrieves whether this server calls System.exit() when shutdown.
792      *
793      * @return true if this server does not call System.exit()
794      *
795      * @jmx.managed-attribute
796      * access="read-write"
797      * description="When Shutdown"
798      */

799     public boolean isNoSystemExit() {
800         return serverProperties.isPropertyTrue(
801             ServerConstants.SC_KEY_NO_SYSTEM_EXIT);
802     }
803
804     /**
805      * Retrieves whether this server restarts on shutdown.
806      *
807      * @return true this server restarts on shutdown
808      *
809      * @jmx.managed-attribute
810      * access="read-write"
811      * description="Automatically?"
812      */

813     public boolean isRestartOnShutdown() {
814         return serverProperties.isPropertyTrue(
815             ServerConstants.SC_KEY_AUTORESTART_SERVER);
816     }
817
818     /**
819      * Retrieves whether silent mode operation was requested in
820      * the server properties.
821      *
822      * @return if true, silent mode was requested, else trace messages
823      * are to be printed
824      *
825      * @jmx.managed-attribute
826      * access="read-write"
827      * description="No trace messages?"
828      */

829     public boolean isSilent() {
830         return isSilent;
831     }
832
833     /**
834      * Retrieves whether the use of secure sockets was requested in the
835      * server properties.
836      *
837      * @return if true, secure sockets are requested, else not
838      *
839      * @jmx.managed-attribute
840      * access="read-write"
841      * description="Use TLS/SSL sockets?"
842      */

843     public boolean isTls() {
844         return serverProperties.isPropertyTrue(ServerConstants.SC_KEY_TLS);
845     }
846
847     /**
848      * Retrieves whether JDBC trace messages are to go to System.out or the
849      * DriverManger PrintStream/PrintWriter, if any.
850      *
851      * @return true if tracing is on (JDBC trace messages to system out)
852      *
853      * @jmx.managed-attribute
854      * access="read-write"
855      * description="JDBC trace messages to System.out?"
856      */

857     public boolean isTrace() {
858         return serverProperties.isPropertyTrue(ServerConstants.SC_KEY_TRACE);
859     }
860
861     /**
862      * Attempts to put properties from the file
863      * with the specified path. The file
864      * extension '.properties' is implicit and should not
865      * be included in the path specification.
866      *
867      * @param path the path of the desired properties file, without the
868      * '.properties' file extension
869      * @throws RuntimeException if this server is running
870      * @return true if the indicated file was read sucessfully, else false
871      *
872      * @jmx.managed-operation
873      * impact="ACTION"
874      * description="Reads in properties"
875      *
876      * @jmx.managed-operation-parameter
877      * name="path"
878      * type="java.lang.String"
879      * position="0"
880      * description="(optional) returns false if path is empty"
881      */

882     public boolean putPropertiesFromFile(String JavaDoc path)
883     throws RuntimeException JavaDoc {
884
885         if (getState() != ServerConstants.SERVER_STATE_SHUTDOWN) {
886             throw new RuntimeException JavaDoc();
887         }
888
889         path = FileUtil.canonicalOrAbsolutePath(path);
890
891         HsqlProperties p = ServerConfiguration.getPropertiesFromFile(path);
892
893         if (p == null || p.isEmpty()) {
894             return false;
895         }
896
897         printWithThread("putPropertiesFromFile(): [" + path + ".properties]");
898         setProperties(p);
899
900         return true;
901     }
902
903     /**
904      * Puts properties from the supplied string argument. The relevant
905      * key value pairs are the same as those for the (web)server.properties
906      * file format, except that the 'server.' prefix should not be specified.
907      *
908      * @param s semicolon-delimited key=value pair string,
909      * e.g. k1=v1;k2=v2;k3=v3...
910      * @throws RuntimeException if this server is running
911      *
912      * @jmx.managed-operation
913      * impact="ACTION"
914      * description="'server.' key prefix automatically supplied"
915      *
916      * @jmx.managed-operation-parameter
917      * name="s"
918      * type="java.lang.String"
919      * position="0"
920      * description="semicolon-delimited key=value pairs"
921      */

922     public void putPropertiesFromString(String JavaDoc s) throws RuntimeException JavaDoc {
923
924         if (getState() != ServerConstants.SERVER_STATE_SHUTDOWN) {
925             throw new RuntimeException JavaDoc();
926         }
927
928         if (StringUtil.isEmpty(s)) {
929             return;
930         }
931
932         printWithThread("putPropertiesFromString(): [" + s + "]");
933
934         HsqlProperties p = HsqlProperties.delimitedArgPairsToProps(s, "=",
935             ";", ServerConstants.SC_KEY_PREFIX);
936
937         setProperties(p);
938     }
939
940     /**
941      * Sets the InetAddress with which this server's ServerSocket will be
942      * constructed. A null or empty string or the special value "0.0.0.0"
943      * can be used to bypass explicit selection, causing the ServerSocket
944      * to be constructed without specifying an InetAddress.
945      *
946      * @param address A string representing the desired InetAddress as would
947      * be retrieved by InetAddres.getByName(), or a null or empty string
948      * or "0.0.0.0" to signify that the server socket should be constructed
949      * using the signature that does not specify the InetAddress.
950      * @throws RuntimeException if this server is running
951      *
952      * @jmx.managed-attribute
953      */

954     public void setAddress(String JavaDoc address) throws RuntimeException JavaDoc {
955
956         checkRunning(false);
957
958         if (org.hsqldb.lib.StringUtil.isEmpty(address)) {
959             address = ServerConstants.SC_DEFAULT_ADDRESS;
960         }
961
962         printWithThread("setAddress(" + address + ")");
963         serverProperties.setProperty(ServerConstants.SC_KEY_ADDRESS, address);
964     }
965
966     /**
967      * Sets the external name (url alias) of the i'th hosted database.
968      *
969      * @param name external name (url alias) of the i'th HSQLDB database
970      * instance this server is to host.
971      * @throws RuntimeException if this server is running
972      *
973      * @jmx.managed-operation
974      * impact="ACTION"
975      * description="Sets the url alias by which is known the i'th hosted Database"
976      *
977      * @jmx.managed-operation-parameter
978      * name="index"
979      * type="int"
980      * position="0"
981      * description="This Server's index for the hosted Database"
982      *
983      * @jmx.managed-operation-parameter
984      * name="name"
985      * type="java.lang.String"
986      * position="1"
987      * description="url alias component for the hosted Database"
988      */

989     public void setDatabaseName(int index,
990                                 String JavaDoc name) throws RuntimeException JavaDoc {
991
992         checkRunning(false);
993         printWithThread("setDatabaseName(" + index + "," + name + ")");
994         serverProperties.setProperty(ServerConstants.SC_KEY_DBNAME + "."
995                                      + index, name);
996     }
997
998     /**
999      * Sets the path of the hosted database.
1000     *
1001     * @param path The path of the i'th HSQLDB database instance this server
1002     * is to host.
1003     *
1004     * @jmx.managed-operation
1005     * impact="ACTION"
1006     * description="Sets the database uri path for the i'th hosted Database"
1007     *
1008     * @jmx.managed-operation-parameter
1009     * name="index"
1010     * type="int"
1011     * position="0"
1012     * description="This Server's index for the hosted Database"
1013     *
1014     * @jmx.managed-operation-parameter
1015     * name="path"
1016     * type="java.lang.String"
1017     * position="1"
1018     * description="database uri path of the hosted Database"
1019     */

1020    public void setDatabasePath(int index,
1021                                String JavaDoc path) throws RuntimeException JavaDoc {
1022
1023        checkRunning(false);
1024        printWithThread("setDatabasePath(" + index + "," + path + ")");
1025        serverProperties.setProperty(ServerConstants.SC_KEY_DATABASE + "."
1026                                     + index, path);
1027    }
1028
1029    /**
1030     * Sets the name of the web page served when no page is specified.
1031     *
1032     * @param file the name of the web page served when no page is specified
1033     *
1034     * @jmx.managed-attribute
1035     */

1036    public void setDefaultWebPage(String JavaDoc file) {
1037
1038        checkRunning(false);
1039        printWithThread("setDefaultWebPage(" + file + ")");
1040
1041        if (serverProtocol != ServerConstants.SC_PROTOCOL_HTTP) {
1042            return;
1043        }
1044
1045        serverProperties.setProperty(ServerConstants.SC_KEY_WEB_DEFAULT_PAGE,
1046                                     file);
1047    }
1048
1049    /**
1050     * Sets the server listen port.
1051     *
1052     * @param port the port at which this server listens
1053     *
1054     * @jmx.managed-attribute
1055     */

1056    public void setPort(int port) throws RuntimeException JavaDoc {
1057
1058        checkRunning(false);
1059        printWithThread("setPort(" + port + ")");
1060        serverProperties.setProperty(ServerConstants.SC_KEY_PORT, port);
1061    }
1062
1063    /**
1064     * Sets the PrintWriter to which server errors are logged. <p>
1065     *
1066     * Setting this attribute to null disables server error logging
1067     *
1068     * @param pw the PrintWriter to which server messages are logged
1069     */

1070    public void setErrWriter(PrintWriter JavaDoc pw) {
1071        errWriter = pw;
1072    }
1073
1074    /**
1075     * Sets the PrintWriter to which server messages are logged. <p>
1076     *
1077     * Setting this attribute to null disables server message logging
1078     *
1079     * @param pw the PrintWriter to which server messages are logged
1080     */

1081    public void setLogWriter(PrintWriter JavaDoc pw) {
1082        logWriter = pw;
1083    }
1084
1085    /**
1086     * Sets whether this server calls System.exit() when shutdown.
1087     *
1088     * @param noExit if true, System.exit() will not be called.
1089     *
1090     * @jmx.managed-attribute
1091     */

1092    public void setNoSystemExit(boolean noExit) {
1093
1094        printWithThread("setNoSystemExit(" + noExit + ")");
1095        serverProperties.setProperty(ServerConstants.SC_KEY_NO_SYSTEM_EXIT,
1096                                     noExit);
1097    }
1098
1099    /**
1100     * Sets whether this server restarts on shutdown.
1101     *
1102     * @param restart if true, this server restarts on shutdown
1103     *
1104     * @jmx.managed-attribute
1105     */

1106    public void setRestartOnShutdown(boolean restart) {
1107
1108        printWithThread("setRestartOnShutdown(" + restart + ")");
1109        serverProperties.setProperty(
1110            ServerConstants.SC_KEY_AUTORESTART_SERVER, restart);
1111    }
1112
1113    /**
1114     * Sets silent mode operation
1115     *
1116     * @param silent if true, then silent mode, else trace messages
1117     * are to be printed
1118     *
1119     * @jmx.managed-attribute
1120     */

1121    public void setSilent(boolean silent) {
1122
1123        printWithThread("setSilent(" + silent + ")");
1124        serverProperties.setProperty(ServerConstants.SC_KEY_SILENT, silent);
1125
1126        isSilent = silent;
1127    }
1128
1129    /**
1130     * Sets whether to use secure sockets
1131     *
1132     * @param tls true for secure sockets, else false
1133     * @throws RuntimeException if this server is running
1134     *
1135     * @jmx.managed-attribute
1136     */

1137    public void setTls(boolean tls) {
1138
1139        checkRunning(false);
1140        printWithThread("setTls(" + tls + ")");
1141        serverProperties.setProperty(ServerConstants.SC_KEY_TLS, tls);
1142    }
1143
1144    /**
1145     * Sets whether trace messages go to System.out or the
1146     * DriverManger PrintStream/PrintWriter, if any.
1147     *
1148     * @param trace if true, route JDBC trace messages to System.out
1149     *
1150     * @jmx.managed-attribute
1151     */

1152    public void setTrace(boolean trace) {
1153
1154        printWithThread("setTrace(" + trace + ")");
1155        serverProperties.setProperty(ServerConstants.SC_KEY_TRACE, trace);
1156        JavaSystem.setLogToSystem(trace);
1157    }
1158
1159    /**
1160     * Sets the path of the root directory from which web content is served.
1161     *
1162     * @param root the root (context) directory from which web content
1163     * is served
1164     *
1165     * @jmx.managed-attribute
1166     */

1167    public void setWebRoot(String JavaDoc root) {
1168
1169        checkRunning(false);
1170
1171        root = (new File JavaDoc(root)).getAbsolutePath();
1172
1173        printWithThread("setWebRoot(" + root + ")");
1174
1175        if (serverProtocol != ServerConstants.SC_PROTOCOL_HTTP) {
1176            return;
1177        }
1178
1179        serverProperties.setProperty(ServerConstants.SC_KEY_WEB_ROOT, root);
1180    }
1181
1182    /**
1183     * Sets server properties using the specified properties object
1184     *
1185     * @param p The object containing properties to set
1186     */

1187    public void setProperties(HsqlProperties p) {
1188
1189        checkRunning(false);
1190
1191        if (p != null) {
1192            serverProperties.addProperties(p);
1193            ServerConfiguration.translateAddressProperty(serverProperties);
1194        }
1195
1196        maxConnections = serverProperties.getIntegerProperty(
1197            ServerConstants.SC_KEY_MAX_CONNECTIONS, 16);
1198
1199        JavaSystem.setLogToSystem(isTrace());
1200
1201        isSilent =
1202            serverProperties.isPropertyTrue(ServerConstants.SC_KEY_SILENT);
1203        isRemoteOpen = serverProperties.isPropertyTrue(
1204            ServerConstants.SC_KEY_REMOTE_OPEN_DB);
1205    }
1206
1207    /**
1208     * Starts this server synchronously. <p>
1209     *
1210     * This method waits for current state to change from
1211     * SERVER_STATE_OPENNING. In order to discover the success or failure
1212     * of this operation, server state must be polled or a subclass of Server
1213     * must be used that overrides the setState method to provide state
1214     * change notification.
1215     *
1216     * @return the server state noted at entry to this method
1217     *
1218     * @jmx.managed-operation
1219     * impact="ACTION_INFO"
1220     * description="Invokes asynchronous startup sequence; returns previous state"
1221     */

1222    public int start() {
1223
1224        printWithThread("start() entered");
1225
1226        int previousState = getState();
1227
1228        if (serverThread != null) {
1229            printWithThread("start(): serverThread != null; no action taken");
1230
1231            return previousState;
1232        }
1233
1234        setState(ServerConstants.SERVER_STATE_OPENING);
1235
1236        serverThread = new ServerThread("HSQLDB Server ");
1237
1238        serverThread.start();
1239
1240        // call synchronized getState() to become owner of the Server Object's monitor
1241
while (getState() == ServerConstants.SERVER_STATE_OPENING) {
1242            try {
1243                Thread.sleep(100);
1244            } catch (InterruptedException JavaDoc e) {}
1245        }
1246
1247        printWithThread("start() exiting");
1248
1249        return previousState;
1250    }
1251
1252    /**
1253     * Stops this server asynchronously. <p>
1254     *
1255     * This method returns immediately, regardless of current state. In order
1256     * to discover the success or failure of this operation, server state must
1257     * be polled or a subclass of Server must be used that overrides the
1258     * setState method to provide state change notification.
1259     *
1260     * @return the server state noted at entry to this method
1261     *
1262     * @jmx.managed-operation
1263     * impact="ACTION_INFO"
1264     * description="Invokes asynchronous shutdown sequence; returns previous state"
1265     */

1266    public int stop() {
1267
1268        printWithThread("stop() entered");
1269
1270        int previousState = getState();
1271
1272        if (serverThread == null) {
1273            printWithThread("stop() serverThread is null; no action taken");
1274
1275            return previousState;
1276        }
1277
1278        releaseServerSocket();
1279        printWithThread("stop() exiting");
1280
1281        return previousState;
1282    }
1283
1284    /**
1285     * Retrieves whether the specified socket should be allowed
1286     * to make a connection. By default, this method always returns
1287     * true, but it can be overidden to implement hosts allow-deny
1288     * functionality.
1289     *
1290     * @param socket the socket to test.
1291     */

1292    protected boolean allowConnection(Socket JavaDoc socket) {
1293        return true;
1294    }
1295
1296    /**
1297     * Initializes this server, setting the accepted connection protocol.
1298     *
1299     * @param protocol typically either SC_PROTOCOL_HTTP or SC_PROTOCOL_HSQL
1300     */

1301    protected void init(int protocol) {
1302
1303        // PRE: This method is only called from the constructor
1304
serverState = ServerConstants.SERVER_STATE_SHUTDOWN;
1305        serverConnSet = new HashSet();
1306        serverId = toString();
1307        serverId = serverId.substring(serverId.lastIndexOf('.') + 1);
1308        serverProtocol = protocol;
1309        serverProperties = ServerConfiguration.newDefaultProperties(protocol);
1310        logWriter = new PrintWriter JavaDoc(System.out);
1311        errWriter = new PrintWriter JavaDoc(System.err);
1312
1313        JavaSystem.setLogToSystem(isTrace());
1314    }
1315
1316    /**
1317     * Sets the server state value.
1318     *
1319     * @param state the new value
1320     */

1321    protected synchronized void setState(int state) {
1322        serverState = state;
1323    }
1324
1325// Package visibility for related classes and intefaces
1326
// that may need to make calls back here.
1327

1328    /**
1329     * This is called from org.hsqldb.DatabaseManager when a database is
1330     * shutdown. This shuts the server down if it is the last database
1331     *
1332     * @param action a code indicating what has happend
1333     */

1334    final void notify(int action, int id) {
1335
1336        printWithThread("notifiy(" + action + "," + id + ") entered");
1337
1338        if (action != ServerConstants.SC_DATABASE_SHUTDOWN) {
1339            return;
1340        }
1341
1342        releaseDatabase(id);
1343
1344        boolean shutdown = true;
1345
1346        for (int i = 0; i < dbID.length; i++) {
1347            if (dbAlias[i] != null) {
1348                shutdown = false;
1349            }
1350        }
1351
1352        if (!isRemoteOpen && shutdown) {
1353            stop();
1354        }
1355    }
1356
1357    /**
1358     * This releases the resources used for a database.
1359     * Is called with id 0 multiple times for non-existent databases
1360     */

1361    final synchronized void releaseDatabase(int id) {
1362
1363        Iterator it;
1364        boolean found = false;
1365
1366        printWithThread("releaseDatabase(" + id + ") entered");
1367
1368        // check all slots as a database may be opened by multiple aliases
1369
for (int i = 0; i < dbID.length; i++) {
1370            if (dbID[i] == id && dbAlias[i] != null) {
1371                dbID[i] = 0;
1372                dbAlias[i] = null;
1373                dbPath[i] = null;
1374                dbType[i] = null;
1375                dbProps[i] = null;
1376            }
1377        }
1378
1379        synchronized (serverConnSet) {
1380            it = new WrapperIterator(serverConnSet.toArray(null));
1381        }
1382
1383        while (it.hasNext()) {
1384            ServerConnection sc = (ServerConnection) it.next();
1385
1386            if (sc.dbID == id) {
1387                sc.signalClose();
1388                serverConnSet.remove(sc);
1389            }
1390        }
1391
1392        printWithThread("releaseDatabase(" + id + ") exiting");
1393    }
1394
1395    /**
1396     * Prints the specified message, s, formatted to identify that the print
1397     * operation is against this server instance.
1398     *
1399     * @param msg The message to print
1400     */

1401    protected synchronized void print(String JavaDoc msg) {
1402
1403        PrintWriter JavaDoc writer = logWriter;
1404
1405        if (writer != null) {
1406            writer.println("[" + serverId + "]: " + msg);
1407            writer.flush();
1408        }
1409    }
1410
1411    /**
1412     * Prints value from server's resource bundle, formatted to
1413     * identify that the print operation is against this server instance.
1414     * Value may be localized according to the default JVM locale
1415     *
1416     * @param key the resource key
1417     */

1418    final void printResource(String JavaDoc key) {
1419
1420        String JavaDoc resource;
1421        StringTokenizer JavaDoc st;
1422
1423        if (serverBundleHandle < 0) {
1424            return;
1425        }
1426
1427        resource = BundleHandler.getString(serverBundleHandle, key);
1428
1429        if (resource == null) {
1430            return;
1431        }
1432
1433        st = new StringTokenizer JavaDoc(resource, "\n\r");
1434
1435        while (st.hasMoreTokens()) {
1436            print(st.nextToken());
1437        }
1438    }
1439
1440    /**
1441     * Prints the stack trace of the Throwable, t, to this Server object's
1442     * errWriter. <p>
1443     *
1444     * @param t the Throwable whose stack trace is to be printed
1445     */

1446    protected synchronized void printStackTrace(Throwable JavaDoc t) {
1447
1448        if (errWriter != null) {
1449            t.printStackTrace(errWriter);
1450            errWriter.flush();
1451        }
1452    }
1453
1454    /**
1455     * Prints the specified message, s, prepended with a timestamp representing
1456     * the current date and time, formatted to identify that the print
1457     * operation is against this server instance.
1458     *
1459     * @param msg the message to print
1460     */

1461    final void printWithTimestamp(String JavaDoc msg) {
1462        print(HsqlDateTime.getSytemTimeString() + " " + msg);
1463    }
1464
1465    /**
1466     * Prints a message formatted similarly to print(String), additionally
1467     * identifying the current (calling) thread. Replaces old method
1468     * trace(String msg).
1469     *
1470     * @param msg the message to print
1471     */

1472    protected void printWithThread(String JavaDoc msg) {
1473
1474        if (!isSilent()) {
1475            print("[" + Thread.currentThread() + "]: " + msg);
1476        }
1477    }
1478
1479    /**
1480     * Prints an error message to this Server object's errWriter.
1481     * The message is formatted similarly to print(String),
1482     * additionally identifying the current (calling) thread.
1483     *
1484     * @param msg the message to print
1485     */

1486    protected synchronized void printError(String JavaDoc msg) {
1487
1488        PrintWriter JavaDoc writer = errWriter;
1489
1490        if (writer != null) {
1491            writer.print("[" + serverId + "]: ");
1492            writer.print("[" + Thread.currentThread() + "]: ");
1493            writer.println(msg);
1494            writer.flush();
1495        }
1496    }
1497
1498    /**
1499     * Prints a description of the request encapsulated by the
1500     * Result argument, r.
1501     *
1502     * Printing occurs iff isSilent() is false. <p>
1503     *
1504     * The message is formatted similarly to print(String), additionally
1505     * indicating the connection identifier. <p>
1506     *
1507     * For Server instances, cid is typically the value assigned to each
1508     * ServerConnection object that is unique amongst all such identifiers
1509     * in each distinct JVM session / class loader
1510     * context. <p>
1511     *
1512     * For WebServer instances, a single logical connection actually spawns
1513     * a new physical WebServerConnection object for each request, so the
1514     * cid is typically the underlying session id, since that does not
1515     * change for the duration of the logical connection.
1516     *
1517     * @param cid the connection identifier
1518     * @param r the request whose description is to be printed
1519     */

1520    final void printRequest(int cid, Result r) {
1521
1522        if (isSilent()) {
1523            return;
1524        }
1525
1526        StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1527
1528        sb.append(cid);
1529        sb.append(':');
1530
1531        switch (r.mode) {
1532
1533            case ResultConstants.SQLPREPARE : {
1534                sb.append("SQLCLI:SQLPREPARE ");
1535                sb.append(r.getMainString());
1536
1537                break;
1538            }
1539            case ResultConstants.SQLEXECDIRECT : {
1540                if (r.getSize() < 2) {
1541                    sb.append(r.getMainString());
1542                } else {
1543                    sb.append("SQLCLI:SQLEXECDIRECT:BATCHMODE\n");
1544
1545                    Iterator it = r.iterator();
1546
1547                    while (it.hasNext()) {
1548                        Object JavaDoc[] data = (Object JavaDoc[]) it.next();
1549
1550                        sb.append(data[0]).append('\n');
1551                    }
1552                }
1553
1554                break;
1555            }
1556            case ResultConstants.SQLEXECUTE : {
1557                sb.append("SQLCLI:SQLEXECUTE:");
1558
1559                if (r.getSize() > 1) {
1560                    sb.append("BATCHMODE:");
1561                }
1562
1563                sb.append(r.getStatementID());
1564
1565/**
1566 * todo - fredt - NOW - fix this without appendStringValueOf
1567 */

1568/*
1569                if (r.getSize() == 1) {
1570                    sb.append('\n');
1571                    StringUtil.appendStringValueOf(r.getParameterData(), sb, true);
1572                }
1573*/

1574                break;
1575            }
1576            case ResultConstants.SQLFREESTMT : {
1577                sb.append("SQLCLI:SQLFREESTMT:");
1578                sb.append(r.getStatementID());
1579
1580                break;
1581            }
1582            case ResultConstants.GETSESSIONATTR : {
1583                sb.append("HSQLCLI:GETSESSIONATTR");
1584
1585                break;
1586            }
1587            case ResultConstants.SETSESSIONATTR : {
1588                sb.append("HSQLCLI:SETSESSIONATTR:");
1589                sb.append("AUTOCOMMIT ");
1590                sb.append(r.rRoot.data[Session.INFO_AUTOCOMMIT]);
1591                sb.append(" CONNECTION_READONLY ");
1592                sb.append(r.rRoot.data[Session.INFO_CONNECTION_READONLY]);
1593
1594                break;
1595            }
1596            case ResultConstants.SQLENDTRAN : {
1597                sb.append("SQLCLI:SQLENDTRAN:");
1598
1599                switch (r.getEndTranType()) {
1600
1601                    case ResultConstants.COMMIT :
1602                        sb.append("COMMIT");
1603                        break;
1604
1605                    case ResultConstants.ROLLBACK :
1606                        sb.append("ROLLBACK");
1607                        break;
1608
1609                    case ResultConstants.SAVEPOINT_NAME_RELEASE :
1610                        sb.append("SAVEPOINT_NAME_RELEASE ");
1611                        sb.append(r.getMainString());
1612                        break;
1613
1614                    case ResultConstants.SAVEPOINT_NAME_ROLLBACK :
1615                        sb.append("SAVEPOINT_NAME_ROLLBACK ");
1616                        sb.append(r.getMainString());
1617                        break;
1618
1619                    default :
1620                        sb.append(r.getEndTranType());
1621                }
1622
1623                break;
1624            }
1625            case ResultConstants.SQLSTARTTRAN : {
1626                sb.append("SQLCLI:SQLSTARTTRAN");
1627
1628                break;
1629            }
1630            case ResultConstants.SQLDISCONNECT : {
1631                sb.append("SQLCLI:SQLDISCONNECT");
1632
1633                break;
1634            }
1635            case ResultConstants.SQLSETCONNECTATTR : {
1636                sb.append("SQLCLI:SQLSETCONNECTATTR:");
1637
1638                switch (r.getConnectionAttrType()) {
1639
1640                    case ResultConstants.SQL_ATTR_SAVEPOINT_NAME : {
1641                        sb.append("SQL_ATTR_SAVEPOINT_NAME ");
1642                        sb.append(r.getMainString());
1643
1644                        break;
1645                    }
1646                    default : {
1647                        sb.append(r.getConnectionAttrType());
1648                    }
1649                }
1650
1651                break;
1652            }
1653            default : {
1654                sb.append("SQLCLI:MODE:");
1655                sb.append(r.mode);
1656
1657                break;
1658            }
1659        }
1660
1661        print(sb.toString());
1662    }
1663
1664    /**
1665     * return database ID
1666     */

1667    synchronized final int getDBID(String JavaDoc aliasPath) throws HsqlException {
1668
1669        int semipos = aliasPath.indexOf(';');
1670        String JavaDoc alias = aliasPath;
1671        String JavaDoc filepath = null;
1672
1673        if (semipos != -1) {
1674            alias = aliasPath.substring(0, semipos);
1675            filepath = aliasPath.substring(semipos + 1);
1676        }
1677
1678        int dbIndex = ArrayUtil.find(dbAlias, alias);
1679
1680        if (dbIndex == -1) {
1681            if (filepath == null) {
1682                RuntimeException JavaDoc e =
1683                    new RuntimeException JavaDoc("database alias does not exist");
1684
1685                printError("database alias=" + alias + " does not exist");
1686                setServerError(e);
1687
1688                throw e;
1689            } else {
1690                return openDatabase(alias, filepath);
1691            }
1692        } else {
1693            return dbID[dbIndex];
1694        }
1695    }
1696
1697    /**
1698     * Open and return database ID
1699     */

1700    final int openDatabase(String JavaDoc alias,
1701                           String JavaDoc filepath) throws HsqlException {
1702
1703        if (!isRemoteOpen) {
1704            RuntimeException JavaDoc e =
1705                new RuntimeException JavaDoc("remote open not allowed");
1706
1707            printError("Remote database open not allowed");
1708            setServerError(e);
1709
1710            throw e;
1711        }
1712
1713        int i = getFirstEmptyDatabaseIndex();
1714
1715        if (i < -1) {
1716            RuntimeException JavaDoc e =
1717                new RuntimeException JavaDoc("limit of open databases reached");
1718
1719            printError("limit of open databases reached");
1720            setServerError(e);
1721
1722            throw e;
1723        }
1724
1725        HsqlProperties newprops = DatabaseURL.parseURL(filepath, false);
1726
1727        if (newprops == null) {
1728            RuntimeException JavaDoc e =
1729                new RuntimeException JavaDoc("invalid database path");
1730
1731            printError("invalid database path");
1732            setServerError(e);
1733
1734            throw e;
1735        }
1736
1737        String JavaDoc path = newprops.getProperty("database");
1738        String JavaDoc type = newprops.getProperty("connection_type");
1739
1740        try {
1741            int dbid = DatabaseManager.getDatabase(type, path, this,
1742                                                   newprops);
1743
1744            dbID[i] = dbid;
1745            dbAlias[i] = alias;
1746            dbPath[i] = path;
1747            dbType[i] = type;
1748            dbProps[i] = newprops;
1749
1750            return dbid;
1751        } catch (HsqlException e) {
1752            printError("Database [index=" + i + "db=" + dbType[i] + dbPath[i]
1753                       + ", alias=" + dbAlias[i] + "] did not open: "
1754                       + e.toString());
1755            setServerError(e);
1756
1757            throw e;
1758        }
1759    }
1760
1761    final int getFirstEmptyDatabaseIndex() {
1762
1763        for (int i = 0; i < dbAlias.length; i++) {
1764            if (dbAlias[i] == null) {
1765                return i;
1766            }
1767        }
1768
1769        return -1;
1770    }
1771
1772    /**
1773     * Opens this server's database instances. This method returns true If
1774     * at least one database goes online, otherwise it returns false.
1775     *
1776     * If openning any of the databases is attempted and an exception is
1777     * thrown, the server error is set to this exception.
1778     *
1779     * @throws HsqlException if a database access error occurs
1780     */

1781    final boolean openDatabases() {
1782
1783        printWithThread("openDatabases() entered");
1784
1785        boolean success = false;
1786
1787        setDBInfoArrays();
1788
1789        for (int i = 0; i < dbAlias.length; i++) {
1790            if (dbAlias[i] == null) {
1791                continue;
1792            }
1793
1794            printWithThread("Opening database: [" + dbType[i] + dbPath[i]
1795                            + "]");
1796
1797            StopWatch sw = new StopWatch();
1798            int id;
1799
1800            try {
1801                id = DatabaseManager.getDatabase(dbType[i], dbPath[i], this,
1802                                                 dbProps[i]);
1803                dbID[i] = id;
1804                success = true;
1805            } catch (HsqlException e) {
1806                printError("Database [index=" + i + "db=" + dbType[i]
1807                           + dbPath[i] + ", alias=" + dbAlias[i]
1808                           + "] did not open: " + e.toString());
1809                setServerError(e);
1810
1811                dbAlias[i] = null;
1812                dbPath[i] = null;
1813                dbType[i] = null;
1814                dbProps[i] = null;
1815
1816                continue;
1817            }
1818
1819            sw.stop();
1820
1821            String JavaDoc msg = "Database [index=" + i + ", id=" + id + ", " + "db="
1822                         + dbType[i] + dbPath[i] + ", alias=" + dbAlias[i]
1823                         + "] opened sucessfully";
1824
1825            print(sw.elapsedTimeToMessage(msg));
1826        }
1827
1828        printWithThread("openDatabases() exiting");
1829
1830        if (isRemoteOpen) {
1831            success = true;
1832        }
1833
1834        if (!success && getServerError() == null) {
1835
1836            // database alias / path list is empty or without full info for any DB
1837
setServerError(Trace.error(Trace.SERVER_NO_DATABASE));
1838        }
1839
1840        return success;
1841    }
1842
1843    /**
1844     * Initialises the database attributes lists from the server properties object.
1845     */

1846    private void setDBInfoArrays() {
1847
1848        dbAlias = getDBNameArray();
1849        dbPath = new String JavaDoc[dbAlias.length];
1850        dbType = new String JavaDoc[dbAlias.length];
1851        dbID = new int[dbAlias.length];
1852        dbProps = new HsqlProperties[dbAlias.length];
1853
1854        for (int i = 0; i < dbAlias.length; i++) {
1855            if (dbAlias[i] == null) {
1856                continue;
1857            }
1858
1859            String JavaDoc path = getDatabasePath(i, true);
1860
1861            if (path == null) {
1862                dbAlias[i] = null;
1863
1864                continue;
1865            }
1866
1867            HsqlProperties dbURL = DatabaseURL.parseURL(path, false);
1868
1869            if (dbURL == null) {
1870                dbAlias[i] = null;
1871
1872                continue;
1873            }
1874
1875            dbPath[i] = dbURL.getProperty("database");
1876            dbType[i] = dbURL.getProperty("connection_type");
1877            dbProps[i] = dbURL;
1878        }
1879    }
1880
1881    /**
1882     * Returns a possibly sparse array of all server.dbname.n values
1883     * from the properties object.
1884     */

1885    private String JavaDoc[] getDBNameArray() {
1886
1887        final String JavaDoc prefix = ServerConstants.SC_KEY_DBNAME + ".";
1888        final int prefixLen = prefix.length();
1889        String JavaDoc[] dblist = new String JavaDoc[10];
1890        int maxindex = 0;
1891
1892        try {
1893            Enumeration JavaDoc en = serverProperties.propertyNames();
1894
1895            for (; en.hasMoreElements(); ) {
1896                String JavaDoc key = (String JavaDoc) en.nextElement();
1897
1898                if (!key.startsWith(prefix)) {
1899                    continue;
1900                }
1901
1902                try {
1903                    int dbnum = Integer.parseInt(key.substring(prefixLen));
1904
1905                    maxindex = dbnum < maxindex ? maxindex
1906                                                : dbnum;
1907                    dblist[dbnum] =
1908                        serverProperties.getProperty(key).toLowerCase();
1909                } catch (NumberFormatException JavaDoc e) {
1910                    printWithThread("dblist: " + e.toString());
1911                }
1912            }
1913        } catch (ArrayIndexOutOfBoundsException JavaDoc e) {
1914            printWithThread("dblist: " + e.toString());
1915        }
1916
1917        return dblist;
1918    }
1919
1920    /**
1921     * Constructs and installs a new ServerSocket instance for this server.
1922     *
1923     * @throws Exception if it is not possible to construct and install
1924     * a new ServerSocket
1925     */

1926    private void openServerSocket() throws Exception JavaDoc {
1927
1928        String JavaDoc address;
1929        int port;
1930        String JavaDoc[] candidateAddrs;
1931        String JavaDoc emsg;
1932        StopWatch sw;
1933
1934        printWithThread("openServerSocket() entered");
1935
1936        if (isTls()) {
1937            printWithThread("Requesting TLS/SSL-encrypted JDBC");
1938        }
1939
1940        sw = new StopWatch();
1941        socketFactory = HsqlSocketFactory.getInstance(isTls());
1942        address = getAddress();
1943        port = getPort();
1944
1945        if (org.hsqldb.lib.StringUtil.isEmpty(address)
1946                || ServerConstants.SC_DEFAULT_ADDRESS.equalsIgnoreCase(
1947                    address.trim())) {
1948            socket = socketFactory.createServerSocket(port);
1949        } else {
1950            try {
1951                socket = socketFactory.createServerSocket(port, address);
1952            } catch (UnknownHostException JavaDoc e) {
1953                candidateAddrs =
1954                    ServerConfiguration.listLocalInetAddressNames();
1955
1956                int messageID;
1957                Object JavaDoc[] messageParameters;
1958
1959                if (candidateAddrs.length > 0) {
1960                    messageID = Trace.Server_openServerSocket;
1961                    messageParameters = new Object JavaDoc[] {
1962                        address, candidateAddrs
1963                    };
1964                } else {
1965                    messageID = Trace.Server_openServerSocket2;
1966                    messageParameters = new Object JavaDoc[]{ address };
1967                }
1968
1969                throw new UnknownHostException JavaDoc(Trace.getMessage(messageID,
1970                        true, messageParameters));
1971            }
1972        }
1973
1974        /*
1975         * Following line necessary for Java 1.3 on UNIX. See accept()
1976         * comment elsewhere in this file.
1977         */

1978        socket.setSoTimeout(1000);
1979        printWithThread("Got server socket: " + socket);
1980        print(sw.elapsedTimeToMessage("Server socket opened successfully"));
1981
1982        if (socketFactory.isSecure()) {
1983            print("Using TLS/SSL-encrypted JDBC");
1984        }
1985
1986        printWithThread("openServerSocket() exiting");
1987    }
1988
1989    /** Prints a timestamped message indicating that this server is online */
1990    private void printServerOnlineMessage() {
1991
1992        String JavaDoc s = getProductName() + " " + getProductVersion()
1993                   + " is online";
1994
1995        printWithTimestamp(s);
1996        printResource("online.help");
1997    }
1998
1999    /**
2000     * Prints a description of the server properties iff !isSilent().
2001     */

2002    protected void printProperties() {
2003
2004        Enumeration JavaDoc e;
2005        String JavaDoc key;
2006        String JavaDoc value;
2007
2008        // Avoid the waste of generating each description,
2009
// only for trace() to silently discard it
2010
if (isSilent()) {
2011            return;
2012        }
2013
2014        e = serverProperties.propertyNames();
2015
2016        while (e.hasMoreElements()) {
2017            key = (String JavaDoc) e.nextElement();
2018            value = serverProperties.getProperty(key);
2019
2020            printWithThread(key + "=" + value);
2021        }
2022    }
2023
2024    /**
2025     * Puts this server into the SERVER_CLOSING state, closes the ServerSocket
2026     * and nullifies the reference to it. If the ServerSocket is already null,
2027     * this method exists immediately, otherwise, the result is to fully
2028     * shut down the server.
2029     */

2030    private void releaseServerSocket() {
2031
2032        printWithThread("releaseServerSocket() entered");
2033
2034        if (socket != null) {
2035            printWithThread("Releasing server socket: [" + socket + "]");
2036            setState(ServerConstants.SERVER_STATE_CLOSING);
2037
2038            try {
2039                socket.close();
2040            } catch (IOException JavaDoc e) {
2041                printError("Exception closing server socket");
2042                printError("releaseServerSocket(): " + e);
2043            }
2044
2045            socket = null;
2046        }
2047
2048        printWithThread("releaseServerSocket() exited");
2049    }
2050
2051    /**
2052     * Attempts to bring this server fully online by opening
2053     * a new ServerSocket, obtaining the hosted databases,
2054     * notifying the status waiter thread (if any) and
2055     * finally entering the listen loop if all else succeeds.
2056     * If any part of the process fails, then this server enters
2057     * its shutdown sequence.
2058     */

2059    private void run() {
2060
2061        StopWatch sw;
2062        ThreadGroup JavaDoc tg;
2063        String JavaDoc tgName;
2064
2065        printWithThread("run() entered");
2066        print("Initiating startup sequence...");
2067        printProperties();
2068
2069        sw = new StopWatch();
2070
2071        setServerError(null);
2072
2073        try {
2074
2075            // Faster init first:
2076
// It is huge waste to fully open the databases, only
2077
// to find that the socket address is already in use
2078
openServerSocket();
2079        } catch (Exception JavaDoc e) {
2080            setServerError(e);
2081            printError("run()/openServerSocket(): ");
2082            printStackTrace(e);
2083            shutdown(true);
2084
2085            return;
2086        }
2087
2088        tgName = "HSQLDB Connections @"
2089                 + Integer.toString(this.hashCode(), 16);
2090        tg = new ThreadGroup JavaDoc(tgName);
2091
2092        tg.setDaemon(false);
2093
2094        serverConnectionThreadGroup = tg;
2095
2096        // Mount the databases this server is supposed to host.
2097
// This may take some time if the databases are not all
2098
// already open.
2099
if (openDatabases() == false) {
2100            setServerError(null);
2101            printError("run()/openDatabases(): ");
2102            shutdown(true);
2103
2104            return;
2105        }
2106
2107        // At this point, we have a valid server socket and
2108
// a valid hosted database set, so its OK to start
2109
// listening for connections.
2110
setState(ServerConstants.SERVER_STATE_ONLINE);
2111        print(sw.elapsedTimeToMessage("Startup sequence completed"));
2112        printServerOnlineMessage();
2113
2114        try {
2115            /*
2116             * This loop is necessary for UNIX w/ Sun Java 1.3 because
2117             * in that case the socket.close() elsewhere will not
2118             * interrupt this accept().
2119             */

2120            while (true) {
2121                try {
2122                    handleConnection(socket.accept());
2123                } catch (java.io.InterruptedIOException JavaDoc e) {}
2124            }
2125        } catch (IOException JavaDoc e) {
2126            if (getState() == ServerConstants.SERVER_STATE_ONLINE) {
2127                setServerError(e);
2128                printError(this + ".run()/handleConnection(): ");
2129                printStackTrace(e);
2130            }
2131        } catch (Throwable JavaDoc t) {
2132            printWithThread(t.toString());
2133        } finally {
2134            shutdown(false); // or maybe getServerError() != null?
2135
}
2136    }
2137
2138    /**
2139     * Sets this Server's last encountered error state.
2140     *
2141     * @param t The new value for the server error
2142     */

2143    protected void setServerError(Throwable JavaDoc t) {
2144        serverError = t;
2145    }
2146
2147    /**
2148     * External method to shut down this server.
2149     */

2150    public void shutdown() {
2151        shutdown(false);
2152    }
2153
2154    /**
2155     * Shuts down this server.
2156     *
2157     * @param error true if shutdown is in response to an error
2158     * state, else false
2159     */

2160    protected synchronized void shutdown(boolean error) {
2161
2162        if (serverState == ServerConstants.SERVER_STATE_SHUTDOWN) {
2163            return;
2164        }
2165
2166        StopWatch sw;
2167
2168        printWithThread("shutdown() entered");
2169
2170        sw = new StopWatch();
2171
2172        print("Initiating shutdown sequence...");
2173        releaseServerSocket();
2174        DatabaseManager.deRegisterServer(this);
2175
2176        if (dbPath != null) {
2177            for (int i = 0; i < dbPath.length; i++) {
2178                releaseDatabase(dbID[i]);
2179            }
2180        }
2181
2182        // Be nice and let applications exit if there are no
2183
// running connection threads
2184
if (serverConnectionThreadGroup != null) {
2185            if (!serverConnectionThreadGroup.isDestroyed()) {
2186                for (int i = 0; serverConnectionThreadGroup.activeCount() > 0;
2187                        i++) {
2188                    int count;
2189
2190                    try {
2191                        Thread.sleep(100);
2192                    } catch (Exception JavaDoc e) {
2193
2194                        // e.getMessage();
2195
}
2196                }
2197
2198                try {
2199                    serverConnectionThreadGroup.destroy();
2200                    printWithThread(serverConnectionThreadGroup.getName()
2201                                    + " destroyed");
2202                } catch (Throwable JavaDoc t) {
2203                    printWithThread(serverConnectionThreadGroup.getName()
2204                                    + " not destroyed");
2205                    printWithThread(t.toString());
2206                }
2207            }
2208
2209            serverConnectionThreadGroup = null;
2210        }
2211
2212        serverThread = null;
2213
2214        setState(ServerConstants.SERVER_STATE_SHUTDOWN);
2215        print(sw.elapsedTimeToMessage("Shutdown sequence completed"));
2216
2217        if (isNoSystemExit()) {
2218            printWithTimestamp("SHUTDOWN : System.exit() was not called");
2219            printWithThread("shutdown() exited");
2220        } else {
2221            printWithTimestamp("SHUTDOWN : System.exit() is called next");
2222            printWithThread("shutdown() exiting...");
2223
2224            try {
2225                System.exit(0);
2226            } catch (Throwable JavaDoc t) {
2227                printWithThread(t.toString());
2228            }
2229        }
2230    }
2231
2232    /**
2233     * Prints message for the specified key, without any special
2234     * formatting. The message content comes from the server
2235     * resource bundle and thus may localized according to the default
2236     * JVM locale.<p>
2237     *
2238     * Uses System.out directly instead of Trace.printSystemOut() so it
2239     * always prints, regardless of Trace settings.
2240     *
2241     * @param key for message
2242     */

2243    protected static void printHelp(String JavaDoc key) {
2244        System.out.print(BundleHandler.getString(serverBundleHandle, key));
2245    }
2246}
2247
Popular Tags