001: /**
002: * com.mckoi.database.DatabaseSystem 12 Aug 2000
003: *
004: * Mckoi SQL Database ( http://www.mckoi.com/database )
005: * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * Version 2 as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License Version 2 for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * Version 2 along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: * Change Log:
021: *
022: *
023: */package com.mckoi.database;
024:
025: import com.mckoi.util.Stats;
026: import com.mckoi.debug.*;
027: import com.mckoi.database.control.DBConfig; //import java.io.File;
028: import java.util.List;
029: import java.util.ArrayList; //import java.util.ResourceBundle;
030: //import java.util.MissingResourceException;
031: import java.util.Properties;
032:
033: /**
034: * This class provides information about shared resources available for the
035: * entire database system running in this VM. Shared information includes
036: * configuration details, DataCellCache, plug-ins, user management, etc.
037: *
038: * @author Tobias Downer
039: */
040:
041: public final class DatabaseSystem extends TransactionSystem {
042:
043: /**
044: * The StatementCache that maintains a cache of parsed queries.
045: */
046: private StatementCache statement_cache = null;
047:
048: /**
049: * True if all queries on the database should be logged in the 'commands.log'
050: * file in the log directory.
051: */
052: private boolean query_logging;
053:
054: /**
055: * The WorkerPool object that manages access to the database(s) in the
056: * system.
057: */
058: private WorkerPool worker_pool;
059:
060: /**
061: * The list of Database objects that this system is being managed by this
062: * VM.
063: */
064: private ArrayList database_list;
065:
066: /**
067: * Set to true when the database is shut down.
068: */
069: private boolean shutdown = false;
070:
071: /**
072: * The UserManager object that handles users connected to the database
073: * engine.
074: */
075: private UserManager user_manager;
076:
077: /**
078: * The thread to run to shut down the database system.
079: */
080: private ShutdownThread shutdown_thread;
081:
082: /**
083: * Constructor.
084: */
085: public DatabaseSystem() {
086: super ();
087: }
088:
089: /**
090: * Inits the DatabaseSystem with the configuration properties of the system.
091: * This can only be called once, and should be called at database boot time.
092: */
093: public void init(DBConfig config) {
094: super .init(config);
095:
096: database_list = new ArrayList();
097:
098: // Create the user manager.
099: user_manager = new UserManager();
100:
101: if (config != null) {
102:
103: boolean status;
104:
105: // Set up the statement cache.
106: status = getConfigBoolean("statement_cache", true);
107: if (status) {
108: statement_cache = new StatementCache(this , 127, 140, 20);
109: }
110: Debug().write(Lvl.MESSAGE, DatabaseSystem.class,
111: "statement_cache = " + status);
112:
113: // The maximum number of worker threads.
114: int max_worker_threads = getConfigInt(
115: "maximum_worker_threads", 4);
116: if (max_worker_threads <= 0) {
117: max_worker_threads = 1;
118: }
119: Debug().write(Lvl.MESSAGE, DatabaseSystem.class,
120: "Max worker threads set to: " + max_worker_threads);
121: worker_pool = new WorkerPool(this , max_worker_threads);
122:
123: // Should we be logging commands?
124: query_logging = getConfigBoolean("query_logging", false);
125:
126: } else {
127: throw new Error("Config bundle already set.");
128: }
129:
130: shutdown = false;
131:
132: }
133:
134: // ---------- Queries ----------
135:
136: /**
137: * If query logging is enabled (all queries are output to 'commands.log' in
138: * the log directory), this returns true. Otherwise it returns false.
139: */
140: public boolean logQueries() {
141: return query_logging;
142: }
143:
144: // ---------- Clean up ----------
145:
146: /**
147: * Disposes all the resources associated with this DatabaseSystem and
148: * invalidates this object.
149: */
150: public void dispose() {
151: super .dispose();
152: worker_pool = null;
153: database_list = null;
154: user_manager = null;
155: }
156:
157: // ---------- Cache Methods ----------
158:
159: /**
160: * Returns the StatementCache that is used to cache StatementTree objects
161: * that are being queried by the database. This is used to reduce the
162: * SQL command parsing overhead.
163: * <p>
164: * If this method returns 'null' then statement caching is disabled.
165: */
166: public StatementCache getStatementCache() {
167: return statement_cache;
168: }
169:
170: // ---------- System preparers ----------
171:
172: /**
173: * Given a Transaction.CheckExpression, this will prepare the expression and
174: * return a new prepared CheckExpression.
175: * <p>
176: * A DatabaseSystem resolves the variables (ignoring case if necessary) and
177: * the functions of the expression.
178: */
179: public Transaction.CheckExpression prepareTransactionCheckConstraint(
180: DataTableDef table_def, Transaction.CheckExpression check) {
181:
182: return super
183: .prepareTransactionCheckConstraint(table_def, check);
184:
185: }
186:
187: // ---------- User management ----------
188:
189: /**
190: * Returns the UserManager object that handles users that are connected
191: * to the database. The aim of this class is to unify the way users are
192: * handled by the engine. It allows us to perform queries to see who's
193: * connected, and any inter-user communication (triggers).
194: */
195: UserManager getUserManager() {
196: return user_manager;
197: }
198:
199: // ---------- Worker Pool Methods ----------
200:
201: /**
202: * Waits until all executing commands have stopped. This is best called
203: * right after a call to 'setIsExecutingCommands(false)'. If these two
204: * commands are run, the database is in a known state where no commands
205: * can be executed.
206: * <p>
207: * NOTE: This can't be called from the WorkerThread. Deadlock will
208: * result if we were allowed to do this.
209: */
210: void waitUntilAllWorkersQuiet() {
211: worker_pool.waitUntilAllWorkersQuiet();
212: }
213:
214: /**
215: * Controls whether the database system is allowed to execute commands or
216: * not. If this is set to true, then calls to 'execute' will be executed
217: * as soon as there is a free worker thread available. Otherwise no
218: * commands are executed until this is enabled.
219: */
220: void setIsExecutingCommands(boolean status) {
221: worker_pool.setIsExecutingCommands(status);
222: }
223:
224: /**
225: * Executes database functions from the 'run' method of the given runnable
226: * instance on the first available worker thread. All database functions
227: * must go through a worker thread. If we ensure this, we can easily stop
228: * all database functions from executing if need be. Also, we only need to
229: * have a certain number of threads active at any one time rather than a
230: * unique thread for each connection.
231: */
232: void execute(User user, DatabaseConnection database, Runnable runner) {
233: worker_pool.execute(user, database, runner);
234: }
235:
236: // ---------- Shut down methods ----------
237:
238: private final ArrayList shut_down_delegates = new ArrayList();
239:
240: /**
241: * Registers the delegate that is executed when the shutdown thread
242: * is activated. Only one delegate may be registered with the database
243: * system. This is only called once and shuts down the relevant
244: * database services.
245: */
246: void registerShutDownDelegate(Runnable delegate) {
247: shut_down_delegates.add(delegate);
248: }
249:
250: /**
251: * The shut down thread. Started when 'shutDown' is called.
252: */
253: private class ShutdownThread extends Thread {
254:
255: private boolean finished = false;
256:
257: synchronized void waitTillFinished() {
258: while (finished == false) {
259: try {
260: wait();
261: } catch (InterruptedException e) {
262: }
263: }
264: }
265:
266: public void run() {
267: synchronized (this ) {
268: if (finished) {
269: return;
270: }
271: }
272:
273: // We need this pause so that the command that executed this shutdown
274: // has time to exit and retrieve the single row result.
275: try {
276: Thread.sleep(1500);
277: } catch (InterruptedException e) {
278: }
279: // Stops commands from being executed by the system...
280: setIsExecutingCommands(false);
281: // Wait until the worker threads are all quiet...
282: waitUntilAllWorkersQuiet();
283:
284: // Close the worker pool
285: worker_pool.shutdown();
286:
287: int sz = shut_down_delegates.size();
288: if (sz == 0) {
289: Debug().write(Lvl.WARNING, this ,
290: "No shut down delegates registered!");
291: } else {
292: for (int i = 0; i < sz; ++i) {
293: Runnable shut_down_delegate = (Runnable) shut_down_delegates
294: .get(i);
295: // Run the shut down delegates
296: shut_down_delegate.run();
297: }
298: shut_down_delegates.clear();
299: }
300:
301: synchronized (this ) {
302: // Wipe all variables from this object
303: dispose();
304:
305: finished = true;
306: notifyAll();
307: }
308: }
309: };
310:
311: /**
312: * This starts the ShutDown thread that is used to shut down the database
313: * server. Since the actual shutdown method is dependent on the type of
314: * database we are running (server or stand-alone) we delegate the
315: * shutdown method to the registered shutdown delegate.
316: */
317: void startShutDownThread() {
318: if (!shutdown) {
319: shutdown = true;
320: shutdown_thread = new ShutdownThread();
321: shutdown_thread.start();
322: }
323: }
324:
325: /**
326: * Returns true if 'shutDown' method has been called.
327: */
328: boolean hasShutDown() {
329: return shutdown;
330: }
331:
332: /**
333: * Wait until the shutdown thread has completed. (Shutdown process
334: * has finished).
335: */
336: void waitUntilShutdown() {
337: shutdown_thread.waitTillFinished();
338: }
339:
340: }
|