KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > jdbc > hsqldb > EmbeddedHSQLDBServer


1 /*
2  * SSL-Explorer
3  *
4  * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */

19             
20 package com.sslexplorer.jdbc.hsqldb;
21
22 import java.io.File JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.PrintWriter JavaDoc;
25 import java.io.Writer JavaDoc;
26 import java.net.Socket JavaDoc;
27 import java.sql.Connection JavaDoc;
28 import java.sql.DriverManager JavaDoc;
29 import java.sql.Statement JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.hsqldb.HsqlProperties;
37 import org.hsqldb.Server;
38 import org.hsqldb.ServerConstants;
39
40 import com.sslexplorer.boot.ContextHolder;
41 import com.sslexplorer.jdbc.JDBCConnectionImpl;
42
43 /**
44  * <p>
45  * Maintains the embedded HSQLDB server engine. When SSL-Explorer is started up,
46  * an instance is created and then {@link #start()}ed.
47  *
48  * <p>
49  * Plugins may then register any new databases they wish to add to the server
50  * (although this should be called indirectly through
51  * {@link com.sslexplorer.core.CoreServlet#addDatabase(String,File)}.
52  *
53  * <p>
54  * When the server is shutting down, the {@link #stop()} method should be called
55  * allow the database engine to clean itself up.
56  *
57  * @author Brett Smith <brett@3sp.com>
58  */

59 public class EmbeddedHSQLDBServer {
60
61     private final static Log log = LogFactory.getLog(EmbeddedHSQLDBServer.class);
62
63     private HsqlProperties properties;
64     private Server server;
65     private boolean testedConnection;
66     private boolean serverMode;
67     private int dbIdx = 1;
68     private List JavaDoc<String JavaDoc> databases;
69     private boolean started;
70
71     /**
72      * Constructor
73      *
74      * @param serverMode if <code>true</code> run HSQLDB in <b>Server</b>
75      * mode, which allows external TCP/IP connections.
76      * @throws Exception
77      */

78     public EmbeddedHSQLDBServer(boolean serverMode) throws Exception JavaDoc {
79         super();
80
81         databases = new ArrayList JavaDoc<String JavaDoc>();
82         this.serverMode = serverMode;
83         if (serverMode) {
84             properties = new HsqlProperties();
85         }
86     }
87
88     /**
89      * Stop the Database engine.
90      */

91     public void stop() {
92
93         if (server != null) {
94
95             /*
96              * TODO A nasty hack. HSQLDB cannot have new databases added to it
97              * while its running in TCP/IP server mode. So we have to restart
98              * the server. Unfortunately, the client side of the connection does
99              * not register that this has happened so is considered re-useable
100              * by the pool. This results in a 'Connection is closed' error when
101              * then next statement executes.
102              */

103             JDBCConnectionImpl.JDBCPool.getInstance().closeAll();
104
105             // Get a JDBC connection
106
for (Iterator JavaDoc i = databases.iterator(); i.hasNext();) {
107                 String JavaDoc n = (String JavaDoc) i.next();
108                 Connection JavaDoc con = null;
109                 try {
110                     if (log.isInfoEnabled())
111                         log.info("Compacting database " + n);
112                     con = DriverManager
113                                     .getConnection(EmbeddedHSQLDBServer.this.serverMode ? "jdbc:hsqldb:hsql://localhost/" + n
114                                                     : "jdbc:hsqldb:file:" + ContextHolder.getContext().getDBDirectory().getPath()
115                                                                     + "/" + n);
116                     Statement JavaDoc s = con.createStatement();
117                     s.execute("SHUTDOWN COMPACT");
118                     if (log.isInfoEnabled())
119                         log.info("Database " + n + " compacted.");
120                 } catch (Exception JavaDoc e) {
121                     log.error("Failed to compact database.");
122                 } finally {
123                     if(con != null) {
124                         try {
125                             con.close();
126                         }
127                         catch(Exception JavaDoc e) {
128                         }
129                     }
130                 }
131
132             }
133             server.signalCloseAllServerConnections();
134             server.stop();
135             waitForServerToStop();
136             server = null;
137             testedConnection = false;
138         }
139         started = false;
140     }
141
142     /**
143      * Start the database engine.
144      *
145      * @throws Exception
146      */

147     public void start() throws Exception JavaDoc {
148         if (serverMode) {
149             if (server == null) {
150                 server = new Server();
151                 server.setLogWriter(log.isDebugEnabled() ? new PrintWriter JavaDoc(new LoggingPrintWriter()) : new PrintWriter JavaDoc(
152                                 new SinkPrintWiter()));
153                 server.setNoSystemExit(true);
154                 server.setProperties(properties);
155             }
156             if (server.getState() != ServerConstants.SERVER_STATE_SHUTDOWN) {
157                 throw new Exception JavaDoc("Cannot start an HSQLDB server that is not shutdown.");
158             }
159             server.start();
160         }
161
162         waitForServer();
163         started = true;
164     }
165
166     void waitForServer() {
167         if (!testedConnection && serverMode) {
168             Socket JavaDoc s = null;
169             String JavaDoc addr = server.getAddress().equals("0.0.0.0") ? "127.0.0.1" : server.getAddress();
170             if (log.isInfoEnabled())
171                 log.info("Waiting for HSQLDB to start accepting connections on " + addr + ":" + server.getPort());
172             for (int i = 0; i < 30; i++) {
173                 try {
174                     s = new Socket JavaDoc(addr, server.getPort());
175                     break;
176                 } catch (IOException JavaDoc ioe) {
177                     try {
178                         Thread.sleep(1000);
179                     } catch (InterruptedException JavaDoc ie) {
180                     }
181                 }
182             }
183             if (s == null) {
184                 throw new IllegalStateException JavaDoc("The HSQLDB server is not accepting connections after 30 seconds.");
185             } else {
186                 testedConnection = true;
187                 if (log.isInfoEnabled())
188                     log.info("HSQLDB is now accepting connections.");
189                 try {
190                     s.close();
191                 } catch (IOException JavaDoc ioe) {
192                 }
193             }
194         }
195     }
196
197     void waitForServerToStop() {
198         if (serverMode) {
199             Socket JavaDoc s = null;
200             String JavaDoc addr = server.getAddress().equals("0.0.0.0") ? "127.0.0.1" : server.getAddress();
201             if (log.isInfoEnabled())
202                 log.info("Waiting for HSQLDB to stop accepting connections on " + addr + ":" + server.getPort());
203             int i = 0;
204             for (; i < 30; i++) {
205                 try {
206                     s = new Socket JavaDoc(addr, server.getPort());
207                     try {
208                         s.close();
209                     } catch (Exception JavaDoc e) {
210                         e.printStackTrace();
211                     }
212                     s = null;
213                     try {
214                         Thread.sleep(1000);
215                     } catch (InterruptedException JavaDoc ie) {
216                     }
217                 } catch (IOException JavaDoc ioe) {
218                     break;
219                 } finally {
220                     if (s != null) {
221                         try {
222                             s.close();
223                         } catch (Exception JavaDoc e) {
224                         }
225                         s = null;
226                     }
227                 }
228             }
229             if (i == 30) {
230                 throw new IllegalStateException JavaDoc("The HSQLDB server has not stopped after 30 seconds.");
231             } else {
232                 if (log.isInfoEnabled())
233                     log.info("HSQLDB is now stopped.");
234             }
235         }
236     }
237
238     /**
239      * Add a new database to be maintained by this engine. If the database files
240      * do not already exist they will be automatically created. If running in
241      * TCP/IP server mode and the database has not been yet been started it will
242      * be.
243      *
244      * @param databaseName
245      * @param file
246      * @throws Exception on any error
247      */

248     public void addDatabase(String JavaDoc databaseName, File JavaDoc file) throws Exception JavaDoc {
249         if (!databases.contains(databaseName)) {
250             if (serverMode) {
251                 if (log.isInfoEnabled())
252                     log.info("Adding database " + databaseName + " in TCP/IP server mode, so restarting database");
253                 boolean wasStarted = started;
254                 if (wasStarted) {
255                     stop();
256                 }
257                 databases.add(databaseName);
258                 dbIdx++;
259                 properties.setProperty("server.database." + dbIdx, "file:" +file.getPath()+ "/" + databaseName);
260                 properties.setProperty("server.dbname." + dbIdx, databaseName);
261                 start();
262             } else {
263                 if (log.isInfoEnabled())
264                     log.info("Adding database " + databaseName + " in embedded mode.");
265                 databases.add(databaseName);
266
267             }
268         }
269
270     }
271
272     /*
273      * Dummy {@link Write} to just sink log output.
274      */

275     class SinkPrintWiter extends Writer JavaDoc {
276         public void close() throws IOException JavaDoc {
277         }
278
279         public void flush() throws IOException JavaDoc {
280         }
281
282         public void write(char[] cbuf, int off, int len) throws IOException JavaDoc {
283         }
284
285     }
286
287     /*
288      * {@link Writer} to just convert log output in Commons Loggin calls.
289      */

290     class LoggingPrintWriter extends Writer JavaDoc {
291
292         private StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
293         private char ch;
294
295         /*
296          * (non-Javadoc)
297          *
298          * @see java.io.Writer#close()
299          */

300         public void close() throws IOException JavaDoc {
301             // not implemented.
302
}
303
304         /*
305          * (non-Javadoc)
306          *
307          * @see java.io.Writer#flush()
308          */

309         public void flush() throws IOException JavaDoc {
310         }
311
312         /*
313          * (non-Javadoc)
314          *
315          * @see java.io.Writer#write(char[], int, int)
316          */

317         public synchronized void write(char[] cbuf, int off, int len) throws IOException JavaDoc {
318             String JavaDoc s = new String JavaDoc(cbuf, off, len);
319             for (int i = 0; i < len; i++) {
320                 ch = s.charAt(i);
321                 if (ch == '\n') {
322                     if (log.isInfoEnabled())
323                         log.info(buffer.toString());
324                     buffer.setLength(0);
325                 } else {
326                     buffer.append(ch);
327                 }
328             }
329         }
330
331     }
332
333 }
Popular Tags