KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jodd > db > pool > CoreConnectionPool


1 // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
2

3 package jodd.db.pool;
4
5 import jodd.db.DbSqlException;
6 import jodd.db.connection.ConnectionProvider;
7
8 import java.sql.Connection JavaDoc;
9 import java.sql.DriverManager JavaDoc;
10 import java.sql.SQLException JavaDoc;
11 import java.util.ArrayList JavaDoc;
12
13 /**
14  * A class for preallocating, recycling, and managing JDBC connections.<br><br>
15  *
16  * It uses threads for opening a new conn. When no conn
17  * availiable it will wait until a conn is released.<br><br>
18  *
19  * todo Replace arraylist with stack
20  * todo Add thread for cleaning non-necessary connections, from time to time.
21  */

22 public class CoreConnectionPool implements Runnable JavaDoc, ConnectionProvider {
23
24     // ---------------------------------------------------------------- properties
25

26     private String JavaDoc driver;
27     private String JavaDoc url;
28     private String JavaDoc user;
29     private String JavaDoc password;
30     private int maxConnections = 10;
31     private int minConnections = 5;
32     private boolean waitIfBusy = false;
33
34     public String JavaDoc getDriver() {
35         return driver;
36     }
37
38     public void setDriver(String JavaDoc driver) {
39         this.driver = driver;
40     }
41
42     public String JavaDoc getUrl() {
43         return url;
44     }
45
46     public void setUrl(String JavaDoc url) {
47         this.url = url;
48     }
49
50     public String JavaDoc getUser() {
51         return user;
52     }
53
54     public void setUser(String JavaDoc user) {
55         this.user = user;
56     }
57
58     public String JavaDoc getPassword() {
59         return password;
60     }
61
62     public void setPassword(String JavaDoc password) {
63         this.password = password;
64     }
65
66     public int getMaxConnections() {
67         return maxConnections;
68     }
69
70     public void setMaxConnections(int maxConnections) {
71         this.maxConnections = maxConnections;
72     }
73
74     public int getMinConnections() {
75         return minConnections;
76     }
77
78     public void setMinConnections(int minConnections) {
79         this.minConnections = minConnections;
80     }
81
82     public boolean isWaitIfBusy() {
83         return waitIfBusy;
84     }
85
86     public void setWaitIfBusy(boolean waitIfBusy) {
87         this.waitIfBusy = waitIfBusy;
88     }
89
90     // ---------------------------------------------------------------- init
91

92     private ArrayList JavaDoc availableConnections, busyConnections;
93     private boolean connectionPending = false;
94
95     public CoreConnectionPool() {
96     }
97
98     public void init() {
99         try {
100             Class.forName(driver);
101         } catch (ClassNotFoundException JavaDoc cnfex) {
102             throw new DbSqlException("Database driver not found: '" + driver + '\'', cnfex);
103         }
104         if (minConnections > maxConnections) {
105             minConnections = maxConnections;
106         }
107         availableConnections = new ArrayList JavaDoc(minConnections);
108         busyConnections = new ArrayList JavaDoc();
109         for (int i = 0; i < minConnections; i++) {
110             try {
111                 availableConnections.add(DriverManager.getConnection(url, user, password));
112             } catch (SQLException JavaDoc sex) {
113                 throw new DbSqlException("Unable to get conn from jdbc driver.", sex);
114             }
115         }
116     }
117
118     // ---------------------------------------------------------------- get/close
119

120     public synchronized Connection JavaDoc getConnection() {
121         if (availableConnections.isEmpty() == false) {
122             int lastIndex = availableConnections.size() - 1;
123             Connection JavaDoc existingConnection = (Connection JavaDoc) availableConnections.get(lastIndex);
124             availableConnections.remove(lastIndex);
125             
126             // If conn on available list is closed (e.g., it timed out), then remove it from available list
127
// and repeat the process of obtaining a conn. Also wake up threads that were waiting for a
128
// conn because maxConnection limit was reached.
129
boolean isClosed;
130             try {
131                 isClosed = existingConnection.isClosed();
132             } catch (SQLException JavaDoc sex) {
133                 throw new DbSqlException("Unable to check if database conn is closed.", sex);
134             }
135             if (isClosed) {
136                 notifyAll(); // freed up a spot for anybody waiting
137
return getConnection();
138             } else {
139                 busyConnections.add(existingConnection);
140                 return existingConnection;
141             }
142         } else {
143             // Three possible cases:
144
// 1) You haven't reached maxConnections limit. So establish one in the background if there isn't
145
// already one pending, then wait for the next available conn (whether or not
146
// it was the newly established one).
147
// 2) You reached maxConnections limit and waitIfBusy flag is false. Throw SQLException in such a case.
148
// 3) You reached maxConnections limit and waitIfBusy flag is true. Then do the same thing as in second
149
// part of step 1: wait for next available conn.
150

151             if (((availableConnections.size() + busyConnections.size()) < maxConnections) && !connectionPending) {
152                 makeBackgroundConnection();
153             } else if (!waitIfBusy) {
154                 throw new DbSqlException("Connection limit " + maxConnections + " reached.");
155             }
156             // wait for either a new conn to be established (if you called makeBackgroundConnection) or for
157
// an existing conn to be freed up.
158
try {
159                 wait();
160             } catch (InterruptedException JavaDoc ie) {
161                 // ignore
162
}
163             // someone freed up a conn, so try again.
164
return getConnection();
165         }
166     }
167
168     /**
169      * You can't just make a new conn in the foreground when none are
170      * available, since this can take several seconds with a slow network
171      * conn. Instead, start a thread that establishes a new conn,
172      * then wait. You get woken up either when the new conn is established
173      * or if someone finishes with an existing conn.
174      */

175     private void makeBackgroundConnection() {
176         connectionPending = true;
177         try {
178             Thread JavaDoc connectThread = new Thread JavaDoc(this);
179             connectThread.start();
180         } catch (OutOfMemoryError JavaDoc oome) {
181             // give up on new conn
182
}
183     }
184
185     public void run() {
186         try {
187             Connection JavaDoc connection = DriverManager.getConnection(url, user, password);
188             synchronized(this) {
189                 availableConnections.add(connection);
190                 connectionPending = false;
191                 notifyAll();
192             }
193         } catch (Exception JavaDoc ex) { // SQLException or OutOfMemory
194
// give up on new conn and wait for existing one to free up.
195
}
196     }
197
198     public synchronized void closeConnection(Connection JavaDoc connection) {
199         busyConnections.remove(connection);
200         availableConnections.add(connection);
201         notifyAll(); // wake up threads that are waiting for a conn
202
}
203
204
205     /**
206      * Returns connection number statistics in the following order:
207      * <ol>
208      * <li>total connections</li>
209      * <li>availiable connections</li>
210      * <li>busy connections</li>
211      * </ol>
212      */

213     public synchronized int[] getConnectionsCount() {
214         return new int[] {availableConnections.size() + busyConnections.size(), availableConnections.size(), busyConnections.size()};
215     }
216
217     // ---------------------------------------------------------------- close
218

219     /**
220      * Close all the connections. Use with caution: be sure no connections are in
221      * use before calling. Note that you are not <I>required</I> to call this
222      * when done with a ConnectionPool, since connections are guaranteed to be
223      * closed when garbage collected. But this method gives more control
224      * regarding when the connections are closed.
225      */

226     public synchronized void close() {
227         closeConnections(availableConnections);
228         availableConnections = new ArrayList JavaDoc();
229         closeConnections(busyConnections);
230         busyConnections = new ArrayList JavaDoc();
231     }
232
233     private void closeConnections(ArrayList JavaDoc connections) {
234         try {
235             for (int i = 0; i < connections.size(); i++) {
236                 Connection JavaDoc connection = (Connection JavaDoc) connections.get(i);
237                 if (!connection.isClosed()) {
238                     connection.close();
239                 }
240             }
241         } catch (SQLException JavaDoc sqle) {
242             // Ignore errors; garbage collect anyhow
243
}
244     }
245 }
246
Popular Tags