KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > DatabaseManager


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.util.Vector JavaDoc;
35
36 import org.hsqldb.lib.FileUtil;
37 import org.hsqldb.lib.HashMap;
38 import org.hsqldb.lib.HashSet;
39 import org.hsqldb.lib.HsqlTimer;
40 import org.hsqldb.lib.IntKeyHashMap;
41 import org.hsqldb.lib.Iterator;
42 import org.hsqldb.persist.HsqlProperties;
43 import org.hsqldb.store.ValuePool;
44
45 /**
46  * Handles initial attempts to connect to HSQLDB databases within the JVM
47  * (or a classloader within the JVM). Opens the database if it is not open
48  * or connects to it if it is already open. This allows the same database to
49  * be used by different instances of Server and by direct connections.<p>
50  *
51  * Maintains a map of Server instances and notifies each server when its
52  * database has shut down.<p>
53  *
54  * Maintains a reference to the timer used for file locks and logging.<p>
55  *
56  * @author fredt@users
57  * @version 1.8.0
58  * @since 1.7.2
59  */

60 public class DatabaseManager {
61
62     // Database and Server registry
63

64     /** provides unique ID's for the Databases currently in registry */
65     private static int dbIDCounter;
66
67     /** name to Database mapping for mem: databases */
68     static final HashMap memDatabaseMap = new HashMap();
69
70     /** File to Database mapping for file: databases */
71     static final HashMap fileDatabaseMap = new HashMap();
72
73     /** File to Database mapping for res: databases */
74     static final HashMap resDatabaseMap = new HashMap();
75
76     /** id number to Database for Databases currently in registry */
77     static final IntKeyHashMap databaseIDMap = new IntKeyHashMap();
78
79     /**
80      * Returns a vector containing the URI (type + path) for all the databases.
81      */

82     public static Vector JavaDoc getDatabaseURIs() {
83
84         Vector JavaDoc v = new Vector JavaDoc();
85         Iterator it = databaseIDMap.values().iterator();
86
87         while (it.hasNext()) {
88             Database db = (Database) it.next();
89
90             v.addElement(db.getURI());
91         }
92
93         return v;
94     }
95
96     /**
97      * Closes all the databases using the given mode.<p>
98      *
99      * CLOSEMODE_IMMEDIATELY = -1;
100      * CLOSEMODE_NORMAL = 0;
101      * CLOSEMODE_COMPACT = 1;
102      * CLOSEMODE_SCRIPT = 2;
103      */

104     public static void closeDatabases(int mode) {
105
106         Iterator it = databaseIDMap.values().iterator();
107
108         while (it.hasNext()) {
109             Database db = (Database) it.next();
110
111             try {
112                 db.close(mode);
113             } catch (HsqlException e) {}
114         }
115     }
116
117     /**
118      * Used by server to open a new session
119      */

120     static Session newSession(int dbID, String JavaDoc user,
121                               String JavaDoc password) throws HsqlException {
122
123         Database db = (Database) databaseIDMap.get(dbID);
124
125         return db == null ? null
126                           : db.connect(user, password);
127     }
128
129     /**
130      * Used by in-process connections and by Servlet
131      */

132
133 // loosecannon1@users 1.7.2 patch properties on the JDBC URL
134
public static Session newSession(String JavaDoc type, String JavaDoc path, String JavaDoc user,
135                                      String JavaDoc password,
136                                      HsqlProperties props)
137                                      throws HsqlException {
138
139         Database db = getDatabase(type, path, props);
140
141         return db == null ? null
142                           : db.connect(user, password);
143     }
144
145     /**
146      * Returns an existing session. Used with repeat HTTP connections
147      * belonging to the same JDBC Conenction / HSQL Session pair.
148      */

149     static Session getSession(int dbId, int sessionId) {
150
151         Database db = (Database) databaseIDMap.get(dbId);
152
153         return db == null ? null
154                           : db.sessionManager.getSession(sessionId);
155     }
156
157     /**
158      * Used by server to open or create a database
159      */

160
161 // loosecannon1@users 1.7.2 patch properties on the JDBC URL
162
static int getDatabase(String JavaDoc type, String JavaDoc path, Server server,
163                            HsqlProperties props) throws HsqlException {
164
165         Database db = getDatabase(type, path, props);
166
167         registerServer(server, db);
168
169         return db.databaseID;
170     }
171
172     /**
173      * This has to be improved once a threading model is in place.
174      * Current behaviour:
175      *
176      * Attempts to connect to different databases do not block. Two db's can
177      * open simultaneously.
178      *
179      * Attempts to connect to a db while it is opening or closing will block
180      * until the db is open or closed. At this point the db state is either
181      * DATABASE_ONLINE (after db.open() has returned) which allows a new
182      * connection to be made, or the state is DATABASE_SHUTDOWN which means
183      * the db can be reopened for the new connection).
184      *
185      */

186
187 // loosecannon1@users 1.7.2 patch properties on the JDBC URL
188
static Database getDatabase(String JavaDoc type, String JavaDoc path,
189                                 HsqlProperties props) throws HsqlException {
190
191         // If the (type, path) pair does not correspond to a registered
192
// instance, then getDatabaseObject() returns a newly constructed
193
// and registered Database instance.
194
// The database state will be DATABASE_SHUTDOWN,
195
// which means that the switch below will attempt to
196
// open the database instance.
197
Database db = getDatabaseObject(type, path, props);
198
199         synchronized (db) {
200             switch (db.getState()) {
201
202                 case Database.DATABASE_ONLINE :
203                     break;
204
205                 case Database.DATABASE_SHUTDOWN :
206
207                     // if the database was shutdown while this attempt
208
// was waiting, add the database back to the registry
209
if (lookupDatabaseObject(type, path) == null) {
210                         addDatabaseObject(type, path, db);
211                     }
212
213                     db.open();
214                     break;
215
216                 // This state will currently not be reached as Database.Close() is
217
// called while a lock is held on the database.
218
// If we remove the lock from this method and a database is
219
// being shutdown by a thread and in the meantime another thread
220
// attempts to connect to the db. The threads could belong to
221
// different server instances or be in-process.
222
case Database.DATABASE_CLOSING :
223
224                 // this case will not be reached as the state is set and
225
// cleared within the db.open() call above, which is called
226
// from this synchronized block
227
// it is here simply as a placeholder for future development
228
case Database.DATABASE_OPENING :
229                     throw Trace.error(Trace.DATABASE_ALREADY_IN_USE,
230                                       Trace.DatabaseManager_getDatabase);
231             }
232         }
233
234         return db;
235     }
236
237 // loosecannon1@users 1.7.2 patch properties on the JDBC URL
238
private static synchronized Database getDatabaseObject(String JavaDoc type,
239             String JavaDoc path, HsqlProperties props) throws HsqlException {
240
241         Database db;
242         String JavaDoc key = path;
243         HashMap databaseMap;
244
245         if (type == DatabaseURL.S_FILE) {
246             databaseMap = fileDatabaseMap;
247             key = filePathToKey(path);
248         } else if (type == DatabaseURL.S_RES) {
249             databaseMap = resDatabaseMap;
250         } else if (type == DatabaseURL.S_MEM) {
251             databaseMap = memDatabaseMap;
252         } else {
253             throw Trace.runtimeError(Trace.UNSUPPORTED_INTERNAL_OPERATION,
254                                      "DatabaseManager.getDatabaseObject");
255         }
256
257         db = (Database) databaseMap.get(key);
258
259         if (db == null) {
260             db = new Database(type, path, type + key, props);
261             db.databaseID = dbIDCounter;
262
263             databaseIDMap.put(dbIDCounter, db);
264
265             dbIDCounter++;
266
267             databaseMap.put(key, db);
268         }
269
270         return db;
271     }
272
273     /**
274      * Looks up database of a given type and path in the registry. Returns
275      * null if there is none.
276      */

277     private static synchronized Database lookupDatabaseObject(String JavaDoc type,
278             String JavaDoc path) throws HsqlException {
279
280         Object JavaDoc key = path;
281         HashMap databaseMap;
282
283         if (type == DatabaseURL.S_FILE) {
284             databaseMap = fileDatabaseMap;
285             key = filePathToKey(path);
286         } else if (type == DatabaseURL.S_RES) {
287             databaseMap = resDatabaseMap;
288         } else if (type == DatabaseURL.S_MEM) {
289             databaseMap = memDatabaseMap;
290         } else {
291             throw (Trace.runtimeError(
292                 Trace.UNSUPPORTED_INTERNAL_OPERATION,
293                 "DatabaseManager.lookupDatabaseObject()"));
294         }
295
296         return (Database) databaseMap.get(key);
297     }
298
299     /**
300      * Adds a database to the registry. Returns
301      * null if there is none.
302      */

303     private static synchronized void addDatabaseObject(String JavaDoc type,
304             String JavaDoc path, Database db) throws HsqlException {
305
306         Object JavaDoc key = path;
307         HashMap databaseMap;
308
309         if (type == DatabaseURL.S_FILE) {
310             databaseMap = fileDatabaseMap;
311             key = filePathToKey(path);
312         } else if (type == DatabaseURL.S_RES) {
313             databaseMap = resDatabaseMap;
314         } else if (type == DatabaseURL.S_MEM) {
315             databaseMap = memDatabaseMap;
316         } else {
317             throw Trace.runtimeError(Trace.UNSUPPORTED_INTERNAL_OPERATION,
318                                      "DatabaseManager.addDatabaseObject()");
319         }
320
321         databaseIDMap.put(db.databaseID, db);
322         databaseMap.put(key, db);
323     }
324
325     /**
326      * Removes the database from registry.
327      */

328     static void removeDatabase(Database database) {
329
330         int dbID = database.databaseID;
331         String JavaDoc type = database.getType();
332         String JavaDoc path = database.getPath();
333         Object JavaDoc key = path;
334         HashMap databaseMap;
335
336         notifyServers(database);
337
338         if (type == DatabaseURL.S_FILE) {
339             databaseMap = fileDatabaseMap;
340
341 // boucherb@users 20040124 - patch 1.7.2
342
// Under the current contract, it's essentially impossible for an
343
// exception to get thrown here, because the database could not
344
// have been registered successfully before hand using the same
345
// path
346
//
347
// Eventually, we might think about storing the key with the
348
// database instance so as to avoid this unnecessary additional
349
// conversion and highly unlikely corner case handling.
350
try {
351                 key = filePathToKey(path);
352             } catch (HsqlException e) {
353                 Iterator it = databaseMap.keySet().iterator();
354                 Object JavaDoc foundKey = null;
355
356                 while (it.hasNext()) {
357                     Object JavaDoc currentKey = it.next();
358
359                     if (databaseMap.get(currentKey) == database) {
360                         foundKey = currentKey;
361
362                         break;
363                     }
364                 }
365
366                 if (foundKey == null) {
367
368                     // ??? return;
369
} else {
370                     key = foundKey;
371                 }
372             }
373         } else if (type == DatabaseURL.S_RES) {
374             databaseMap = resDatabaseMap;
375         } else if (type == DatabaseURL.S_MEM) {
376             databaseMap = memDatabaseMap;
377         } else {
378             throw (Trace.runtimeError(
379                 Trace.UNSUPPORTED_INTERNAL_OPERATION,
380                 "DatabaseManager.lookupDatabaseObject()"));
381         }
382
383         databaseIDMap.remove(dbID);
384         databaseMap.remove(key);
385
386         if (databaseIDMap.isEmpty()) {
387             ValuePool.resetPool();
388         }
389     }
390
391     /**
392      * Maintains a map of servers to sets of databases.
393      * Servers register each of their databases.
394      * When a database is shutdown, all the servers accessing it are notified.
395      * The database is then removed form the sets for all servers and the
396      * servers that have no other database are removed from the map.
397      */

398     static HashMap serverMap = new HashMap();
399
400     /**
401      * Deregisters a server completely.
402      */

403     static void deRegisterServer(Server server) {
404         serverMap.remove(server);
405     }
406
407     /**
408      * Deregisters a server as serving a given database. Not yet used.
409      */

410     private static void deRegisterServer(Server server, Database db) {
411
412         Iterator it = serverMap.values().iterator();
413
414         for (; it.hasNext(); ) {
415             HashSet databases = (HashSet) it.next();
416
417             databases.remove(db);
418
419             if (databases.isEmpty()) {
420                 it.remove();
421             }
422         }
423     }
424
425     /**
426      * Registers a server as serving a given database.
427      */

428     private static void registerServer(Server server, Database db) {
429
430         if (!serverMap.containsKey(server)) {
431             serverMap.put(server, new HashSet());
432         }
433
434         HashSet databases = (HashSet) serverMap.get(server);
435
436         databases.add(db);
437     }
438
439     /**
440      * Notifies all servers that serve the database that the database has been
441      * shutdown.
442      */

443     private static void notifyServers(Database db) {
444
445         Iterator it = serverMap.keySet().iterator();
446
447         for (; it.hasNext(); ) {
448             Server server = (Server) it.next();
449             HashSet databases = (HashSet) serverMap.get(server);
450
451             if (databases.contains(db)) {
452                 server.notify(ServerConstants.SC_DATABASE_SHUTDOWN,
453                               db.databaseID);
454             }
455         }
456     }
457
458     static boolean isServerDB(Database db) {
459
460         Iterator it = serverMap.keySet().iterator();
461
462         for (; it.hasNext(); ) {
463             Server server = (Server) it.next();
464             HashSet databases = (HashSet) serverMap.get(server);
465
466             if (databases.contains(db)) {
467                 return true;
468             }
469         }
470
471         return false;
472     }
473
474     // Timer
475
private static final HsqlTimer timer = new HsqlTimer();
476
477     public static HsqlTimer getTimer() {
478         return timer;
479     }
480
481     // converts file path to database lookup key, converting any
482
// thrown exception to an HsqlException in the process
483
private static String JavaDoc filePathToKey(String JavaDoc path) throws HsqlException {
484
485         try {
486             return FileUtil.canonicalPath(path);
487         } catch (Exception JavaDoc e) {
488             throw Trace.error(Trace.FILE_IO_ERROR, e.toString());
489         }
490     }
491 }
492
Popular Tags