001: // ConnectionManager.java
002: // $Id: ConnectionManager.java,v 1.17 2001/03/13 14:07:57 ylafon Exp $
003: // (c) COPYRIGHT MIT, INRIA and Keio, 2000.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005: package org.w3c.tools.jdbc;
006:
007: import java.sql.SQLException;
008:
009: import java.util.Collections;
010: import java.util.Comparator;
011: import java.util.Properties;
012: import java.util.LinkedList;
013:
014: import org.w3c.util.LRUList;
015: import org.w3c.util.SyncLRUList;
016: import org.w3c.util.PropertyMonitoring;
017: import org.w3c.util.ObservableProperties;
018:
019: /**
020: * @version $Revision: 1.17 $
021: * @author Benoît Mahé (bmahe@w3.org)
022: */
023:
024: class ManagerDescription {
025: ConnectionManager manager = null;
026: Properties properties = null;
027:
028: final ConnectionManager getManager() {
029: return manager;
030: }
031:
032: final boolean sameProperties(Properties props) {
033: return (Jdbc.getMaxConn(props) == Jdbc.getMaxConn(properties));
034: }
035:
036: ManagerDescription(ConnectionManager manager, Properties props) {
037: this .manager = manager;
038: this .properties = props;
039: }
040: }
041:
042: public class ConnectionManager implements PropertyMonitoring {
043:
044: public final static long WAIT_TIMEOUT = 1000 * 60 * 2; // 2 minutes
045:
046: public final static boolean debug = false;
047:
048: public final static int STATE_UNCHANGED = 0;
049: public final static int STATE_CHANGED = 1;
050:
051: protected int state = STATE_UNCHANGED;
052:
053: protected int conn_count = 0;
054: protected int conn_max;
055: protected int conn2free;
056:
057: Properties props = null;
058:
059: /**
060: * To keep track of existing managers
061: */
062: private static ManagerDescription managers[] = null;
063:
064: /**
065: * The LRU list of idle connections.
066: */
067: protected LRUList connectionsLru = null;
068:
069: /**
070: * The LRU list of used connection
071: */
072: protected LinkedList usedConnections = null;
073:
074: /**
075: * PropertyMonitoring implementation.
076: */
077: public boolean propertyChanged(String name) {
078: if (name.equals(Jdbc.MAX_CONNECTIONS_P)) {
079: conn_max = Jdbc.getMaxConn(props);
080: conn2free = Math.max(conn_max / 5, 1);
081: return true;
082: }
083: return false;
084: }
085:
086: /**
087: * Get a connection to the given server.
088: * @param server the jdbc server.
089: * @return a JdbcConnection or null if there is no connection available.
090: */
091: protected JdbcConnection getConnection(JdbcServer server) {
092: JdbcServerState ss = server.getState();
093: JdbcConnection conn = null;
094:
095: while (true) {
096: synchronized (this ) {
097: while (true) {
098: conn = ss.getConnection();
099: if (conn == null)
100: break;
101: if (debug) {
102: System.err.println("Reusing connection. ");
103: }
104: if (conn.markUsed())
105: return conn;
106: }
107: if (negotiateConnection(server)) {
108: conn = allocateConnection(server);
109: if (conn.markUsed()) {
110: return conn;
111: } else {
112: // Failed to establish a fresh connection !
113: return null;
114: }
115: }
116: }
117: // Wait for a connection to become available:
118: try {
119: waitForConnection(server);
120: } catch (InterruptedException ex) {
121: }
122: if (state == STATE_UNCHANGED) {
123: // timeout reached
124: if (debug) {
125: System.err.println(">>> Wait timeout reached.");
126: }
127: freeConnections();
128: }
129: }
130: }
131:
132: protected Comparator conn_comparator = new Comparator() {
133: public int compare(Object o1, Object o2) {
134: if ((o1 instanceof JdbcConnection)
135: && (o2 instanceof JdbcConnection)) {
136: JdbcConnection conn1 = (JdbcConnection) o1;
137: JdbcConnection conn2 = (JdbcConnection) o2;
138: if (conn1.isClosed()) {
139: if (conn2.isClosed()) {
140: return 0;
141: }
142: return -1;
143: }
144: return (int) (conn2.getQueryStamp() - conn1
145: .getQueryStamp());
146: }
147: throw new ClassCastException("can't compare");
148: }
149: };
150:
151: /**
152: * PANIC, we need to close some used connection.
153: */
154: protected synchronized void freeConnections() {
155: if (debug) {
156: System.err.println(">>> Closing waiting connection");
157: }
158: Collections.sort(usedConnections, conn_comparator);
159: int size = usedConnections.size();
160: int i = (conn2free > size) ? size : conn2free;
161: while (i-- > 0) {
162: JdbcConnection conn = (JdbcConnection) usedConnections
163: .removeFirst();
164: // delete it
165: conn.delete();
166: }
167: }
168:
169: /**
170: * Connections management - Allocate a new connection for this server.
171: * @param server the JdbcServer
172: * @return a newly created connection or null
173: */
174: protected JdbcConnection allocateConnection(JdbcServer server) {
175: if (debug) {
176: System.err
177: .println("Allocating new connection to " + server);
178: }
179: JdbcConnection conn = new JdbcConnection(server);
180: notifyConnection(conn);
181: return conn;
182: }
183:
184: protected boolean negotiateConnection(JdbcServer server) {
185: if (conn_count >= conn_max) {
186: // remove the oldest idle Connection
187: JdbcConnection conn = (JdbcConnection) connectionsLru
188: .removeTail();
189: if (conn == null) { // no idle connection
190: return false;
191: } else {
192: if (debug) {
193: System.err.println("DELETING IDLE CONNECTION!!!");
194: }
195: conn.delete();
196: }
197: }
198: return true;
199: }
200:
201: protected void deleteConnection(JdbcConnection conn) {
202: JdbcServerState ss = conn.getServer().getState();
203: ss.deleteConnection(conn);
204: usedConnections.remove(conn);
205: synchronized (this ) {
206: --conn_count;
207: if (debug) {
208: System.err.println("+++ delete conn_count: "
209: + conn_count);
210: }
211: state = STATE_CHANGED;
212: notifyAll();
213: }
214: }
215:
216: /**
217: * A new connection has just been created.
218: * @param conn the new connection
219: */
220: protected synchronized void notifyConnection(JdbcConnection conn) {
221: ++conn_count;
222: }
223:
224: /**
225: * The given connection is about to be used.
226: * Update our list of available servers.
227: * @param conn The idle connection.
228: */
229: public void notifyUse(JdbcConnection conn) {
230: if (debug) {
231: System.err.println("+++ JdbcConnection used [" + conn_count
232: + "]");
233: }
234: connectionsLru.remove(conn);
235: usedConnections.add(conn);
236: }
237:
238: /**
239: * The given connection can be reused, but is now idle.
240: * @param conn The connection that is now idle.
241: */
242: public synchronized void notifyIdle(JdbcConnection conn) {
243: if (debug) {
244: System.err.println("+++ JdbcConnection idle [" + conn_count
245: + "]");
246: }
247: usedConnections.remove(conn);
248: connectionsLru.toHead(conn);
249: JdbcServerState ss = conn.getServer().getState();
250: ss.registerConnection(conn);
251: state = STATE_CHANGED;
252: notifyAll();
253: }
254:
255: /**
256: * Wait for a connection to come up.
257: * @param server, the target server.
258: * @exception InterruptedException If interrupted..
259: */
260:
261: protected synchronized void waitForConnection(JdbcServer server)
262: throws InterruptedException {
263: state = STATE_UNCHANGED;
264: wait(WAIT_TIMEOUT);
265: }
266:
267: /**
268: * Get an instance of the Jdbc manager.
269: * This method returns an actual instance of the Jdbc manager. It may
270: * return different managers, if it decides to distribute the load on
271: * different managers (avoid the ConnectionManager being a bottleneck).
272: * @return An application wide instance of the Jdbc manager.
273: */
274:
275: public static synchronized ConnectionManager getManager(Properties p) {
276: if (managers != null) {
277: for (int i = 0; i < managers.length; i++) {
278: if (managers[i] == null)
279: continue;
280: if (managers[i].sameProperties(p)) {
281: return managers[i].getManager();
282: }
283: }
284: }
285: ConnectionManager manager = new ConnectionManager(p);
286: if (managers != null) {
287: ManagerDescription nm[] = new ManagerDescription[managers.length + 1];
288: System.arraycopy(managers, 0, nm, 0, managers.length);
289: nm[managers.length] = new ManagerDescription(manager, p);
290: } else {
291: managers = new ManagerDescription[1];
292: managers[0] = new ManagerDescription(manager, p);
293: }
294: return manager;
295: }
296:
297: public static ConnectionManager getManager() {
298: return getManager(System.getProperties());
299: }
300:
301: private ConnectionManager(Properties p) {
302: this .props = p;
303: this .connectionsLru = new SyncLRUList();
304: this .usedConnections = new LinkedList();
305: if (props instanceof ObservableProperties) {
306: ((ObservableProperties) props).registerObserver(this );
307: }
308: this .conn_max = Jdbc.getMaxConn(props);
309: this .conn2free = Math.max(conn_max / 5, 1);
310: }
311:
312: }
|