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

org.hsqldb.DatabaseManager Maven / Gradle / Ivy

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


package org.hsqldb;

import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.FileUtil;
import org.hsqldb.lib.HashMap;
import org.hsqldb.lib.HashSet;
import org.hsqldb.lib.HsqlTimer;
import org.hsqldb.lib.IntKeyHashMap;
import org.hsqldb.lib.Iterator;
import org.hsqldb.lib.Notified;
import org.hsqldb.map.ValuePool;
import org.hsqldb.persist.HsqlProperties;

/**
 * Handles initial attempts to connect to HSQLDB databases within the JVM
 * (or a classloader within the JVM). Opens the database if it is not open
 * or connects to it if it is already open. This allows the same database to
 * be used by different instances of Server and by direct connections.

* * Maintains a map of Server instances and notifies each server when its * database has shut down.

* * Maintains a reference to the timer used for file locks and logging.

* * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.5.0 * @since 1.7.2 */ public class DatabaseManager { // Database and Server registry /** provides unique ID's for the Databases currently in registry */ private static AtomicInteger dbIDCounter = new AtomicInteger(); /** name to Database mapping for mem: databases */ static final HashMap memDatabaseMap = new HashMap(); /** File to Database mapping for file: databases */ static final HashMap fileDatabaseMap = new HashMap(); /** File to Database mapping for res: databases */ static final HashMap resDatabaseMap = new HashMap(); /** id number to Database for Databases currently in registry */ static final IntKeyHashMap databaseIDMap = new IntKeyHashMap(); /** * Returns a vector containing the URI (type + path) for all the databases. */ public static Vector getDatabaseURIs() { Vector v = new Vector(); synchronized (databaseIDMap) { Iterator it = databaseIDMap.values().iterator(); while (it.hasNext()) { Database db = (Database) it.next(); v.addElement(db.getURI()); } } return v; } /** * Closes all the databases using the given mode.

* * CLOSEMODE_IMMEDIATELY = 1; * CLOSEMODE_NORMAL = 2; * CLOSEMODE_COMPACT = 3; * CLOSEMODE_SCRIPT = 4; */ public static void closeDatabases(int mode) { synchronized (databaseIDMap) { Iterator it = databaseIDMap.values().iterator(); while (it.hasNext()) { Database db = (Database) it.next(); try { db.close(mode); } catch (HsqlException e) {} } } } /** * Used by server to open a new session */ public static Session newSession(int dbID, String user, String password, String zoneString, int timeZoneSeconds) { Database db = null; synchronized (databaseIDMap) { db = (Database) databaseIDMap.get(dbID); } if (db == null) { return null; } Session session = db.connect(user, password, zoneString, timeZoneSeconds); session.isNetwork = true; return session; } /** * Used by in-process connections and by Servlet */ public static Session newSession(String type, String path, String user, String password, HsqlProperties props, String zoneString, int timeZoneSeconds) { Database db = getDatabase(type, path, props); return db.connect(user, password, zoneString, timeZoneSeconds); } /** * Returns an existing session. Used with repeat HTTP connections * belonging to the same JDBC Connection / HSQL Session pair. */ public static Session getSession(int dbId, long sessionId) { Database db = null; synchronized (databaseIDMap) { db = (Database) databaseIDMap.get(dbId); } return db == null ? null : db.sessionManager.getSession(sessionId); } /** * Used by server to open or create a database */ public static int getDatabase(String type, String path, Notified server, HsqlProperties props) { Database db = getDatabase(type, path, props); registerServer(server, db); return db.databaseID; } public static Database getDatabase(int id) { synchronized (databaseIDMap) { return (Database) databaseIDMap.get(id); } } public static void shutdownDatabases(Notified server, int shutdownMode) { Database[] dbArray; synchronized (serverMap) { HashSet databases = (HashSet) serverMap.get(server); if (databases == null) { dbArray = new Database[0]; } else { dbArray = new Database[databases.size()]; databases.toArray(dbArray); } } for (int i = 0; i < dbArray.length; i++) { dbArray[i].close(shutdownMode); } } /** * This has to be improved once a threading model is in place. * Current behaviour: * * Attempts to connect to different databases do not block. Two db's can * open simultaneously. * * Attempts to connect to a db while it is opening or closing will block * until the db is open or closed. At this point the db state is either * DATABASE_ONLINE (after db.open() has returned) which allows a new * connection to be made, or the state is DATABASE_SHUTDOWN which means * the db can be reopened for the new connection). * */ public static Database getDatabase(String dbtype, String path, HsqlProperties props) { // If the (type, path) pair does not correspond to a registered // instance, then getDatabaseObject() returns a newly constructed // and registered Database instance. // The database state will be DATABASE_SHUTDOWN, // which means that the switch below will attempt to // open the database instance. DatabaseType type = DatabaseType.get(dbtype); Database db = getDatabaseObject(type, path, props); synchronized (db) { switch (db.getState()) { case Database.DATABASE_ONLINE : break; case Database.DATABASE_SHUTDOWN : // if the database was shutdown while this attempt // was waiting, add the database back to the registry if (lookupDatabaseObject(type, path) == null) { addDatabaseObject(type, path, db); } db.open(); break; // This state will currently not be reached as Database.Close() is // called while a lock is held on the database. // If we remove the lock from this method and a database is // being shutdown by a thread and in the meantime another thread // attempts to connect to the db. The threads could belong to // different server instances or be in-process. case Database.DATABASE_CLOSING : // this case will not be reached as the state is set and // cleared within the db.open() call above, which is called // from this synchronized block // it is here simply as a placeholder for future development case Database.DATABASE_OPENING : throw Error.error(ErrorCode.LOCK_FILE_ACQUISITION_FAILURE, ErrorCode.M_DatabaseManager_getDatabase); } } return db; } private static synchronized Database getDatabaseObject(DatabaseType type, String path, HsqlProperties props) { Database db; String key = path; HashMap databaseMap; switch (type) { case DB_FILE : { databaseMap = fileDatabaseMap; key = filePathToKey(path); synchronized (databaseMap) { db = (Database) databaseMap.get(key); if (db == null) { if (databaseMap.size() > 0) { Iterator it = databaseMap.keySet().iterator(); while (it.hasNext()) { String current = (String) it.next(); if (key.equalsIgnoreCase(current)) { key = current; break; } } } } } break; } case DB_RES : { databaseMap = resDatabaseMap; break; } case DB_MEM : { databaseMap = memDatabaseMap; break; } default : throw Error.runtimeError(ErrorCode.U_S0500, "DatabaseManager"); } synchronized (databaseMap) { db = (Database) databaseMap.get(key); } if (db == null) { db = new Database(type, path, key, props); db.databaseID = dbIDCounter.getAndIncrement(); synchronized (databaseIDMap) { databaseIDMap.put(db.databaseID, db); } synchronized (databaseMap) { databaseMap.put(key, db); } } return db; } /** * Looks up database of a given type and path in the registry. Returns * null if there is none. */ public static synchronized Database lookupDatabaseObject(DatabaseType type, String path) { Object key = path; HashMap databaseMap; switch (type) { case DB_FILE : databaseMap = fileDatabaseMap; key = filePathToKey(path); break; case DB_RES : databaseMap = resDatabaseMap; break; case DB_MEM : databaseMap = memDatabaseMap; break; default : throw (Error.runtimeError(ErrorCode.U_S0500, "DatabaseManager")); } synchronized (databaseMap) { return (Database) databaseMap.get(key); } } /** * Adds a database to the registry. */ private static synchronized void addDatabaseObject(DatabaseType type, String path, Database db) { Object key = path; HashMap databaseMap; switch (type) { case DB_FILE : databaseMap = fileDatabaseMap; key = filePathToKey(path); break; case DB_RES : databaseMap = resDatabaseMap; break; case DB_MEM : databaseMap = memDatabaseMap; break; default : throw (Error.runtimeError(ErrorCode.U_S0500, "DatabaseManager")); } synchronized (databaseIDMap) { databaseIDMap.put(db.databaseID, db); } synchronized (databaseMap) { databaseMap.put(key, db); } } /** * Removes the database from registry. */ static void removeDatabase(Database database) { int dbID = database.databaseID; DatabaseType type = database.getType(); String path = database.getPath(); Object key = path; HashMap databaseMap; notifyServers(database); if (type == DatabaseType.DB_FILE) { databaseMap = fileDatabaseMap; key = filePathToKey(path); } else if (type == DatabaseType.DB_RES) { databaseMap = resDatabaseMap; } else if (type == DatabaseType.DB_MEM) { databaseMap = memDatabaseMap; } else { throw (Error.runtimeError(ErrorCode.U_S0500, "DatabaseManager")); } boolean isEmpty = false; synchronized (databaseIDMap) { databaseIDMap.remove(dbID); isEmpty = databaseIDMap.isEmpty(); } synchronized (databaseMap) { databaseMap.remove(key); } if (isEmpty) { ValuePool.resetPool(); } } /** * Maintains a map of servers to sets of databases. * Servers register each of their databases. * When a database is shutdown, all the servers accessing it are notified. * The database is then removed form the sets for all servers and the * servers that have no other database are removed from the map. */ static final HashMap serverMap = new HashMap(); /** * Deregisters a server completely. */ public static void deRegisterServer(Notified server) { synchronized (serverMap) { serverMap.remove(server); } } /** * Registers a server as serving a given database. */ private static void registerServer(Notified server, Database db) { synchronized (serverMap) { if (!serverMap.containsKey(server)) { serverMap.put(server, new HashSet()); } HashSet databases = (HashSet) serverMap.get(server); databases.add(db); } } /** * Notifies all servers that serve the database that the database has been * shutdown. */ private static void notifyServers(Database db) { Notified[] servers; synchronized (serverMap) { servers = new Notified[serverMap.size()]; serverMap.keysToArray(servers); } for (int i = 0; i < servers.length; i++) { Notified server = servers[i]; HashSet databases; boolean removed = false; synchronized (serverMap) { databases = (HashSet) serverMap.get(server); } if (databases != null) { synchronized (databases) { removed = databases.remove(db); } } if (removed) { server.notify(db.databaseID); } } } static boolean isServerDB(Database db) { Iterator it = serverMap.keySet().iterator(); for (; it.hasNext(); ) { Notified server = (Notified) it.next(); HashSet databases = (HashSet) serverMap.get(server); if (databases.contains(db)) { return true; } } return false; } // Timer private static final HsqlTimer timer = new HsqlTimer(); public static HsqlTimer getTimer() { return timer; } // converts file path to database lookup key, converting any // thrown exception to an HsqlException in the process private static String filePathToKey(String path) { try { return FileUtil.getFileUtil().canonicalPath(path); } catch (Exception e) { return path; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy