Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/roller/util/ConnectionPool.java


1   
2   package org.roller.util;
3   
4   import java.sql.*;
5   import java.util.*;
6   
7   /** A class for preallocating, recycling, and managing
8    *  JDBC connections.
9    *  <P>
10   *  Taken from Core Servlets and JavaServer Pages
11   *  from Prentice Hall and Sun Microsystems Press,
12   *  http://www.coreservlets.com/.
13   *  &copy; 2000 Marty Hall; may be freely used or adapted.
14   */
15  
16  public class ConnectionPool implements Runnable {
17    private String driver, url, username, password;
18    private int maxConnections;
19    private boolean waitIfBusy;
20    private Vector availableConnections, busyConnections;
21    private boolean connectionPending = false;
22  
23    public ConnectionPool(String driver, String url,
24                          String username, String password,
25                          int initialConnections,
26                          int maxConnections,
27                          boolean waitIfBusy)
28        throws SQLException {
29      this.driver = driver;
30      this.url = url;
31      this.username = username;
32      this.password = password;
33      this.maxConnections = maxConnections;
34      this.waitIfBusy = waitIfBusy;
35      if (initialConnections > maxConnections) {
36        initialConnections = maxConnections;
37      }
38      availableConnections = new Vector(initialConnections);
39      busyConnections = new Vector();
40      for(int i=0; i<initialConnections; i++) {
41        availableConnections.addElement(makeNewConnection());
42      }
43    }
44    
45    public synchronized Connection getConnection()
46        throws SQLException {
47      if (!availableConnections.isEmpty()) {
48        Connection existingConnection =
49          (Connection)availableConnections.lastElement();
50        int lastIndex = availableConnections.size() - 1;
51        availableConnections.removeElementAt(lastIndex);
52        // If connection on available list is closed (e.g.,
53        // it timed out), then remove it from available list
54        // and repeat the process of obtaining a connection.
55        // Also wake up threads that were waiting for a
56        // connection because maxConnection limit was reached.
57        if (existingConnection.isClosed()) {
58          notifyAll(); // Freed up a spot for anybody waiting
59          return(getConnection());
60        } else {
61          busyConnections.addElement(existingConnection);
62          return(existingConnection);
63        }
64      } else {
65        
66        // Three possible cases:
67        // 1) You haven't reached maxConnections limit. So
68        //    establish one in the background if there isn't
69        //    already one pending, then wait for
70        //    the next available connection (whether or not
71        //    it was the newly established one).
72        // 2) You reached maxConnections limit and waitIfBusy
73        //    flag is false. Throw SQLException in such a case.
74        // 3) You reached maxConnections limit and waitIfBusy
75        //    flag is true. Then do the same thing as in second
76        //    part of step 1: wait for next available connection.
77        
78        if ((totalConnections() < maxConnections) &&
79            !connectionPending) {
80          makeBackgroundConnection();
81        } else if (!waitIfBusy) {
82          throw new SQLException("Connection limit reached");
83        }
84        // Wait for either a new connection to be established
85        // (if you called makeBackgroundConnection) or for
86        // an existing connection to be freed up.
87        try {
88          wait();
89        } catch(InterruptedException ie) {}
90        // Someone freed up a connection, so try again.
91        return(getConnection());
92      }
93    }
94  
95    // You can't just make a new connection in the foreground
96    // when none are available, since this can take several
97    // seconds with a slow network connection. Instead,
98    // start a thread that establishes a new connection,
99    // then wait. You get woken up either when the new connection
100   // is established or if someone finishes with an existing
101   // connection.
102 
103   private void makeBackgroundConnection() {
104     connectionPending = true;
105     try {
106       Thread connectThread = new Thread(this);
107       connectThread.start();
108     } catch(OutOfMemoryError oome) {
109       // Give up on new connection
110     }
111   }
112 
113   public void run() {
114     try {
115       Connection connection = makeNewConnection();
116       synchronized(this) {
117         availableConnections.addElement(connection);
118         connectionPending = false;
119         notifyAll();
120       }
121     } catch(Exception e) { // SQLException or OutOfMemory
122       // Give up on new connection and wait for existing one
123       // to free up.
124     }
125   }
126 
127   // This explicitly makes a new connection. Called in
128   // the foreground when initializing the ConnectionPool,
129   // and called in the background when running.
130   
131   private Connection makeNewConnection()
132       throws SQLException {
133     try {
134       // Load database driver if not already loaded
135       Class.forName(driver);
136       // Establish network connection to database
137       Connection connection =
138         DriverManager.getConnection(url, username, password);
139       return(connection);
140     } catch(ClassNotFoundException cnfe) {
141       // Simplify try/catch blocks of people using this by
142       // throwing only one exception type.
143       throw new SQLException("Can't find class for driver: " +
144                              driver);
145     }
146   }
147 
148   public synchronized void free(Connection connection) {
149     busyConnections.removeElement(connection);
150     availableConnections.addElement(connection);
151     // Wake up threads that are waiting for a connection
152     notifyAll(); 
153   }
154     
155   public synchronized int totalConnections() {
156     return(availableConnections.size() +
157            busyConnections.size());
158   }
159 
160   /** Close all the connections. Use with caution:
161    *  be sure no connections are in use before
162    *  calling. Note that you are not <I>required</I> to
163    *  call this when done with a ConnectionPool, since
164    *  connections are guaranteed to be closed when
165    *  garbage collected. But this method gives more control
166    *  regarding when the connections are closed.
167    */
168 
169   public synchronized void closeAllConnections() {
170     closeConnections(availableConnections);
171     availableConnections = new Vector();
172     closeConnections(busyConnections);
173     busyConnections = new Vector();
174   }
175 
176   private void closeConnections(Vector connections) {
177     try {
178       for(int i=0; i<connections.size(); i++) {
179         Connection connection =
180           (Connection)connections.elementAt(i);
181         if (!connection.isClosed()) {
182           connection.close();
183         }
184       }
185     } catch(SQLException sqle) {
186       // Ignore errors; garbage collect anyhow
187     }
188   }
189   
190   public synchronized String toString() {
191     String info =
192       "ConnectionPool(" + url + "," + username + ")" +
193       ", available=" + availableConnections.size() +
194       ", busy=" + busyConnections.size() +
195       ", max=" + maxConnections;
196     return(info);
197   }
198 }
199