001: /**
002: * com.mckoi.database.jdbcserver.TCPServer 21 Jul 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.jdbcserver;
024:
025: import com.mckoi.database.DatabaseSystem;
026: import com.mckoi.database.Database;
027: import com.mckoi.debug.*;
028: import java.net.ServerSocket;
029: import java.net.Socket;
030: import java.net.InetAddress;
031: import java.io.IOException;
032: import java.util.HashMap;
033: import java.util.ResourceBundle;
034:
035: /**
036: * A TCP/IP socket server that opens a single port and allows JDBC clients
037: * to connect through the port to talk with the database.
038: *
039: * @author Tobias Downer
040: */
041:
042: public final class TCPServer {
043:
044: /**
045: * The parent Database object that describes everything about the
046: * database this TCP server is for.
047: */
048: private Database database;
049:
050: /**
051: * The ConnectionPoolServer that polls the ServerConnection for new commands
052: * to process.
053: */
054: private ConnectionPoolServer connection_pool;
055:
056: /**
057: * The ServerSocket object where the database server is bound.
058: */
059: private ServerSocket server_socket;
060:
061: /**
062: * The InetAddress the JDBC server is bound to.
063: */
064: private InetAddress address;
065:
066: /**
067: * The port the JDBC server is on.
068: */
069: private int port;
070:
071: /**
072: * The connection pool model used for this server.
073: */
074: private String connection_pool_model;
075:
076: /**
077: * Constructs the TCPServer over the given DatabaseSystem configuration.
078: */
079: public TCPServer(Database database) {
080: this .database = database;
081: }
082:
083: /**
084: * Returns a DebugLogger object that we can log debug messages to.
085: */
086: public final DebugLogger Debug() {
087: return database.Debug();
088: }
089:
090: /**
091: * Returns the port the JDBC server is on.
092: */
093: public int getJDBCPort() {
094: return port;
095: }
096:
097: /**
098: * Checks to see if there's already something listening on the jdbc
099: * port. Returns true if the jdbc port in the configuration is available,
100: * otherwise returns false.
101: */
102: public boolean checkAvailable(InetAddress bind_address, int tcp_port) {
103: // MAJOR MAJOR HACK: We attempt to bind to the JDBC Port and if we get
104: // an error then most likely there's already a database running on this
105: // host.
106:
107: int port = tcp_port;
108: // // Get information about how to set up the TCP port from the config
109: // // bundle.
110: // int port = Integer.parseInt(config.getString("jdbc_server_port"));
111:
112: try {
113: // Bind the ServerSocket objects to the ports.
114: server_socket = new ServerSocket(port, 50, bind_address);
115: server_socket.close();
116: } catch (IOException e) {
117: // If error then return false.
118: return false;
119: }
120:
121: return true;
122: }
123:
124: /**
125: * Starts the server running. This method returns immediately but spawns
126: * its own thread.
127: */
128: public void start(InetAddress bind_address, int tcp_port,
129: String connection_pool_model) {
130:
131: this .address = bind_address;
132: this .port = tcp_port;
133: this .connection_pool_model = connection_pool_model;
134:
135: // // Get information about how to set up the TCP port from the config
136: // // bundle.
137: // port = Integer.parseInt(config.getString("jdbc_server_port"));
138: //
139: // // The 'tcp_connection_pool_thread_model' property determines the
140: // // connection pool object to use.
141: // connection_pool_model = "multi_threaded";
142: // try {
143: // String cptm = config.getString("tcp_connection_pool_thread_model");
144: // if (cptm.equalsIgnoreCase("single_threaded")) {
145: // connection_pool_model = "single_threaded";
146: // }
147: // // Multi-threaded if 'tcp_connection_pool_thread_model' is anything
148: // // other than 'single_threaded'
149: // }
150: // catch (java.util.MissingResourceException e) {
151: // // If no property in the config assume multi-threaded
152: // }
153: // Choose our connection pool implementation
154: if (connection_pool_model.equals("multi_threaded")) {
155: this .connection_pool = new MultiThreadedConnectionPoolServer(
156: database);
157: } else if (connection_pool_model.equals("single_threaded")) {
158: this .connection_pool = new SingleThreadedConnectionPoolServer(
159: database);
160: }
161:
162: try {
163: // Bind the ServerSocket object to the port.
164: server_socket = new ServerSocket(port, 50, bind_address);
165: server_socket.setSoTimeout(0);
166: } catch (IOException e) {
167: Debug().writeException(e);
168: Debug().write(Lvl.ERROR, this ,
169: "Unable to start a server socket on port: " + port);
170: throw new Error(e.getMessage());
171: }
172:
173: // This thread blocks on the server socket.
174: Thread listen_thread = new Thread() {
175: public void run() {
176: try {
177: // Accept new connections and notify when one arrives
178: while (true) {
179: Socket s = server_socket.accept();
180: portConnection(s);
181: }
182: } catch (IOException e) {
183: Debug().writeException(Lvl.WARNING, e);
184: Debug().write(Lvl.WARNING, this ,
185: "Socket listen thread died.");
186: }
187: }
188: };
189:
190: // listen_thread.setDaemon(true);
191: listen_thread.setName("Mckoi - TCP/IP Socket Accept");
192:
193: listen_thread.start();
194:
195: }
196:
197: /**
198: * Called whenever a new connection has been received on the port.
199: */
200: private void portConnection(Socket socket) throws IOException {
201: // TCP connections are formatted as;
202: // 'TCP/[ip address]:[remote port]:[local port]'
203: String host_string = "TCP/"
204: + socket.getInetAddress().getHostAddress() + ":"
205: + socket.getPort() + "@"
206: + socket.getLocalAddress().getHostAddress() + ":"
207: + socket.getLocalPort();
208: // String host_string =
209: // "Host [" + socket.getInetAddress().getHostAddress() + "] " +
210: // "port=" + socket.getPort() + " localport=" + socket.getLocalPort();
211: // Make a new DatabaseInterface for this connection,
212: JDBCDatabaseInterface db_interface = new JDBCDatabaseInterface(
213: database, host_string);
214: TCPJDBCServerConnection connection = new TCPJDBCServerConnection(
215: db_interface, socket, Debug());
216: // Add the provider onto the queue of providers that are serviced by
217: // the server.
218: connection_pool.addConnection(connection);
219: }
220:
221: /**
222: * Closes the JDBC Server.
223: */
224: public void close() {
225: if (server_socket != null) {
226: try {
227: server_socket.close();
228: } catch (IOException e) {
229: Debug().write(Lvl.ERROR, this ,
230: "Error closing JDBC Server.");
231: Debug().writeException(e);
232: }
233: }
234: connection_pool.close();
235: }
236:
237: /**
238: * Returns human understandable information about the server.
239: */
240: public String toString() {
241: StringBuffer buf = new StringBuffer();
242: buf.append("TCP JDBC Server (");
243: buf.append(connection_pool_model);
244: buf.append(") on ");
245: if (address != null) {
246: buf.append(address.getHostAddress());
247: buf.append(" ");
248: }
249: buf.append("port: ");
250: buf.append(getJDBCPort());
251: return new String(buf);
252: }
253:
254: }
|