001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Contact: sequoia@continuent.org
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License");
008: * you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: *
019: * Initial developer(s): Emmanuel Cecchet.
020: * Contributor(s): ______________________________________.
021: */package org.continuent.sequoia.driver;
022:
023: import java.util.ArrayList;
024:
025: /**
026: * The <code>ConnectionClosingThread</code> wakes up every 5 seconds when
027: * close() has been called on a connection and it frees the connection if it has
028: * not been reused.
029: *
030: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
031: * @version 1.0
032: */
033: public class ConnectionClosingThread extends Thread {
034: /* wait time before closing a connection in ms */
035: private static final int WAIT_TIME = 5000;
036:
037: private Driver driver;
038: private ArrayList pendingConnectionClosing;
039:
040: /**
041: * Builds a new ConnectionClosingThread
042: *
043: * @param driver The driver that created us
044: */
045: public ConnectionClosingThread(Driver driver) {
046: super ("ConnectionClosingThread");
047: this .driver = driver;
048: this .pendingConnectionClosing = driver.pendingConnectionClosing;
049: driver.connectionClosingThreadisAlive = true;
050: }
051:
052: /**
053: * The connection closing thread wakes up every WAIT_TIME seconds when close()
054: * has been called on a connection and it frees the connection if it has not
055: * been reused.
056: */
057: public void run() {
058: try {
059: Connection firstConnectionToClose = null;
060: Connection lastConnectionToClose = null;
061: int pendingConnectionSize;
062: ArrayList closingList = new ArrayList();
063: boolean killed = false;
064:
065: while (!killed) {
066: synchronized (pendingConnectionClosing) {
067: pendingConnectionSize = pendingConnectionClosing
068: .size();
069: if (pendingConnectionSize == 0)
070: break;
071:
072: try {
073: // Look at the connections in the queue before sleeping
074: firstConnectionToClose = (Connection) pendingConnectionClosing
075: .get(0);
076: lastConnectionToClose = (Connection) pendingConnectionClosing
077: .get(pendingConnectionSize - 1);
078:
079: // Sleep
080: pendingConnectionClosing.wait(WAIT_TIME);
081: } catch (InterruptedException ignore) {
082: }
083:
084: pendingConnectionSize = pendingConnectionClosing
085: .size();
086: // Exit, no more connections
087: if (pendingConnectionSize == 0)
088: break;
089:
090: // Compare the queue now with its state when we got to sleep
091: if (firstConnectionToClose == pendingConnectionClosing
092: .get(0)) { // Ok, the connection has not been reused, let's close it
093: if (lastConnectionToClose == (Connection) pendingConnectionClosing
094: .get(pendingConnectionSize - 1)) { // No connection has been reused, remove them all
095: closingList
096: .addAll(pendingConnectionClosing);
097: pendingConnectionClosing.clear();
098: killed = true; // Let's die, there are no more connections
099: } else
100: // Close only the first connection
101: closingList.add(pendingConnectionClosing
102: .remove(0));
103: }
104: }
105:
106: // Effectively close the connections outside the synchronized block
107: while (!closingList.isEmpty())
108: closeConnection((Connection) closingList.remove(0));
109: }
110: } catch (RuntimeException e) {
111: e.printStackTrace();
112: } finally {
113: synchronized (pendingConnectionClosing) {
114: driver.connectionClosingThreadisAlive = false;
115: }
116: }
117: }
118:
119: /**
120: * Closes a connection. This cleanup should belong to the underlying class.
121: *
122: * @param c the connection to close
123: */
124: private void closeConnection(Connection c) {
125: try {
126: // Free remote resources
127: if (c.socketOutput != null) {
128: c.reallyClose();
129: // The following probably better belongs to Connection.reallyClose(),
130: // so there would be no external class messing up with c.socketXXXput,,
131: // and Connection can close itself.
132: c.socketOutput.flush();
133: if (c.socketInput != null) { // Wait for the controller to receive the connection and close the
134: // stream. If we do not wait for the controller ack, the connection is
135: // closed on the controller before the closing is handled which
136: // results in an ugly warning message on the controller side. We are
137: // not in a hurry when closing the connection so let do the things
138: // nicely!
139: c.socketInput.readBoolean();
140: c.socketInput.close();
141: }
142: c.socketOutput.close();
143: }
144:
145: if (c.socket != null)
146: c.socket.close();
147: } catch (Exception ignore) {
148: }
149: }
150:
151: }
|