001: /**
002: * com.mckoi.database.jdbcserver.MultiThreadedConnectionPoolServer 21 Jun 2001
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.jdbcserver;
024:
025: import com.mckoi.database.User;
026: import com.mckoi.database.Database;
027: import com.mckoi.database.DatabaseSystem;
028: import com.mckoi.debug.*;
029: import java.io.IOException;
030: import java.util.ArrayList;
031: import java.util.LinkedList;
032: import java.util.ResourceBundle;
033:
034: /**
035: * A multi-threaded implementation of a connection pool server. This starts
036: * a new thread for each connection made and processes each command as they
037: * arrive.
038: *
039: * @author Tobias Downer
040: */
041:
042: final class MultiThreadedConnectionPoolServer implements
043: ConnectionPoolServer {
044:
045: /**
046: * If this is set to true then the server periodically outputs statistics
047: * about the connections.
048: */
049: private static final boolean DISPLAY_STATS = false;
050:
051: /**
052: * The Database parent.
053: */
054: private Database database;
055:
056: /**
057: * The list of all threads that were created to deal with incoming
058: * commands.
059: */
060: private ArrayList client_threads;
061:
062: /**
063: * The Constructor. The argument is the configuration file.
064: */
065: MultiThreadedConnectionPoolServer(Database database) {
066: this .database = database;
067: client_threads = new ArrayList();
068: }
069:
070: /**
071: * Returns a DebugLogger object that we can log debug messages to.
072: */
073: public final DebugLogger Debug() {
074: return database.Debug();
075: }
076:
077: /**
078: * Connects a new ServerConnection into the pool of connections to clients
079: * that this server maintains. We then cycle through these connections
080: * determining whether any commands are pending. If a command is pending
081: * we spawn off a worker thread to do the task.
082: */
083: public void addConnection(ServerConnection connection) {
084: ClientThread client_thread = new ClientThread(connection);
085: synchronized (client_threads) {
086: client_threads.add(client_thread);
087: }
088: client_thread.start();
089: }
090:
091: /**
092: * Closes this connection pool server down.
093: */
094: public void close() {
095: synchronized (client_threads) {
096: int size = client_threads.size();
097: for (int i = 0; i < size; ++i) {
098: ((ClientThread) client_threads.get(i)).close();
099: }
100: }
101: }
102:
103: // ---------- Inner classes ----------
104:
105: /**
106: * This thread blocks waiting for a complete command to arrive from the
107: * client it is connected to.
108: */
109: private class ClientThread extends Thread {
110:
111: /**
112: * The ServerConnection object being serviced by this thread.
113: */
114: private ServerConnection server_connection;
115:
116: /**
117: * If this is set to true, the thread run method should close off.
118: */
119: private boolean client_closed;
120:
121: /**
122: * This is set to true if we are processing a request from the client.
123: */
124: private boolean processing_command;
125:
126: /**
127: * The Constructor.
128: */
129: public ClientThread(ServerConnection connection) {
130: super ();
131: // setPriority(NORM_PRIORITY - 1);
132: setName("Mckoi - Client Connection");
133:
134: this .server_connection = connection;
135: client_closed = false;
136: processing_command = false;
137: }
138:
139: /**
140: * Checks each connection in the 'service_connection_list' list. If there
141: * is a command pending, and any previous commands on this connection have
142: * completed, then this will spawn off a new process to deal with the
143: * command.
144: */
145: private void checkCurrentConnection()
146: throws InterruptedException {
147: try {
148:
149: // Wait if we are processing a command
150: synchronized (this ) {
151: while (processing_command) {
152: if (client_closed) {
153: return;
154: }
155: // Wait 2 minutes just to make sure we don't miss a poll,
156: wait(120000);
157: }
158: }
159:
160: // Block until a complete command is available
161: server_connection.blockForRequest();
162:
163: processing_command = true;
164:
165: database.execute(null, null, new Runnable() {
166: public void run() {
167:
168: try {
169: // Process the next request that's pending.
170: server_connection.processRequest();
171: } catch (IOException e) {
172: Debug().writeException(Lvl.INFORMATION, e);
173: } finally {
174: // Not processing a command anymore so notify the ClientThread
175: processing_command = false;
176: synchronized (ClientThread.this ) {
177: ClientThread.this .notifyAll();
178: }
179: }
180:
181: }
182: });
183:
184: } catch (IOException e) {
185: // If an IOException is generated, we must remove this provider from
186: // the list.
187: close();
188:
189: // This happens if the connection closes.
190: Debug().write(
191: Lvl.INFORMATION,
192: this ,
193: "IOException generated while checking connections, "
194: + "removing provider.");
195: Debug().writeException(Lvl.INFORMATION, e);
196: }
197:
198: }
199:
200: /**
201: * Call this method to stop the thread.
202: */
203: public synchronized void close() {
204: client_closed = true;
205: try {
206: server_connection.close();
207: } catch (Throwable e) {
208: // ignore
209: }
210: notifyAll();
211: }
212:
213: /**
214: * The Runnable method of the farmer thread.
215: */
216: public void run() {
217: while (true) {
218: try {
219:
220: boolean closed = false;
221: synchronized (this ) {
222: closed = client_closed;
223: }
224: // Exit if the farmer thread has been closed...
225: if (closed == true) {
226: // Remove this thread from the list of client threads.
227: synchronized (client_threads) {
228: client_threads.remove(this );
229: }
230: return;
231: }
232:
233: checkCurrentConnection();
234:
235: } catch (Throwable e) {
236: Debug().write(Lvl.ERROR, this ,
237: "Connection Pool Farmer Error");
238: Debug().writeException(e);
239: }
240: }
241: }
242:
243: };
244:
245: }
|