001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: DefaultConnectionPool.java 468638 2006-10-28 06:52:06Z minchau $
020     */
021    package org.apache.xalan.lib.sql;
022    
023    import java.sql.Connection;
024    import java.sql.DatabaseMetaData;
025    import java.sql.Driver;
026    import java.sql.DriverManager;
027    import java.sql.SQLException;
028    import java.util.Enumeration;
029    import java.util.Iterator;
030    import java.util.Properties;
031    import java.util.Vector;
032    
033    import org.apache.xalan.res.XSLMessages;
034    import org.apache.xalan.res.XSLTErrorResources;
035    
036    /**
037     * For internal connectiones, i.e. Connection information supplies in the
038     * Stylesheet. The Default Connection Pool will be used.
039     */
040    public class DefaultConnectionPool implements ConnectionPool
041    {
042      /**
043       * A placeholder thast will keep the driver loaded
044       * between calls.
045       */
046      private Driver m_Driver = null;
047      /**
048       */
049      private static final boolean DEBUG = false;
050    
051      /**
052       * The basic information to make a JDBC Connection
053       */
054      private String m_driver = new String("");
055      /**
056       */
057      private String m_url = new String("");
058    
059    
060      /**
061       * The mimimum size of the connection pool, if the
062       * number of available connections falls below this
063       * mark, min connections will be allocated. The Connection
064       * Pool will always be somewhere between MinSize and MinSize*2
065       */
066      private int m_PoolMinSize = 1;
067    
068    
069      /**
070       * Always implement the properties mechinism, if the Password
071       * or Username is set seperatly then we will add them to the
072       * property manually.
073       */
074      private Properties m_ConnectionProtocol = new Properties();
075    
076      /**
077       * Storage for the PooledConnections
078       */
079      private Vector m_pool = new Vector();
080    
081      /**
082       * Are we active ??
083       */
084      private boolean m_IsActive = false;
085    
086      /**
087       */
088      public DefaultConnectionPool( ) {}
089    
090    
091      /**
092       * Return our current Active state
093       *
094       */
095      public boolean isEnabled( )
096      {
097        return m_IsActive;
098      }
099    
100      /**
101       * Set the driver call to be used to create connections
102       * @param d
103       *
104       */
105      public void setDriver( String d )
106      {
107        m_driver = d;
108      }
109    
110      /**
111       * Set the url used to connect to the database
112       * @param url
113       *
114       */
115      public void setURL( String url )
116      {
117        m_url = url;
118      }
119    
120      /**
121       * Go through the connection pool and release any connections
122       * that are not InUse;
123       *
124       */
125      public void freeUnused( )
126      {
127        // Iterate over the entire pool closing the
128        // JDBC Connections.
129        Iterator i = m_pool.iterator();
130        while(i.hasNext())
131        {
132          PooledConnection pcon =
133            (PooledConnection) i.next();
134    
135          // If the PooledConnection is not in use, close it
136          if ( pcon.inUse() == false )
137          {
138            if (DEBUG)
139            {
140              System.err.println("Closing JDBC Connection ");
141            }
142            
143            pcon.close();
144            i.remove();        
145          }
146        }
147    
148      }
149    
150      /**
151       * Is our ConnectionPool have any connections that are still in Use ??
152       *
153       */
154      public boolean hasActiveConnections( )
155      {
156        return (m_pool.size() > 0);
157      }
158    
159    
160      /**
161       * Set the password in the property set.
162       * @param p
163       *
164       */
165      public void setPassword( String p )
166      {
167        m_ConnectionProtocol.put("password", p);
168      }
169    
170      /**
171       * Set the user name in the property set
172       * @param u
173       *
174       */
175      public void setUser( String u )
176      {
177        m_ConnectionProtocol.put("user", u);
178      }
179    
180      /**
181       * The Protocol string is used to pass in other connection
182       * properties. A properties file is a general purpose container
183       *
184       * @param p
185       *
186       */
187      public void setProtocol( Properties p )
188      {
189        Enumeration e = p.keys();
190        while (e.hasMoreElements())
191        {
192          String key = (String) e.nextElement();
193          m_ConnectionProtocol.put(key, p.getProperty(key));
194        }
195      }
196    
197    
198      /**
199       * Override the current number of connections to keep in the pool. This
200       * setting will only have effect on a new pool or when a new connection
201       * is requested and there is less connections that this setting.
202       * @param n
203       *
204       */
205      public void setMinConnections( int n )
206      {
207        m_PoolMinSize = n;
208      }
209    
210      /**
211       * Try to aquire a new connection, if it succeeds then return
212       * true, else return false.
213       * Note: This method will cause the connection pool to be built.
214       *
215       */
216      public boolean testConnection( )
217      {
218        try
219        {
220          if (DEBUG)
221          {
222            System.out.println("Testing Connection");
223          }
224    
225          Connection conn = getConnection();
226    
227          if (DEBUG)
228          {
229            DatabaseMetaData dma = conn.getMetaData();
230    
231            System.out.println("\nConnected to " + dma.getURL());
232            System.out.println("Driver   " + dma.getDriverName());
233            System.out.println("Version  " + dma.getDriverVersion());
234            System.out.println("");
235          }
236    
237          if (conn == null) return false;
238    
239          releaseConnection(conn);
240    
241          if (DEBUG)
242          {
243            System.out.println("Testing Connection, SUCCESS");
244          }
245    
246          return true;
247        }
248        catch(Exception e)
249        {
250          if (DEBUG)
251          {
252            System.out.println("Testing Connection, FAILED");
253            e.printStackTrace();
254          }
255    
256          return false;
257        }
258    
259      }
260    
261    
262      // Find an available connection
263      /**
264       * @return Connection
265       * @throws SQLException
266       * @throws IllegalArgumentException
267       */
268      public synchronized Connection getConnection( )throws IllegalArgumentException, SQLException
269      {
270    
271        PooledConnection pcon = null;
272    
273        // We will fill up the pool any time it is less than the
274        // Minimum. THis could be cause by the enableing and disabling
275        // or the pool.
276        //
277        if ( m_pool.size() < m_PoolMinSize ) { initializePool(); }
278    
279        // find a connection not in use
280        for ( int x = 0; x < m_pool.size(); x++ )
281        {
282    
283          pcon = (PooledConnection) m_pool.elementAt(x);
284    
285          // Check to see if the Connection is in use
286          if ( pcon.inUse() == false )
287          {
288            // Mark it as in use
289            pcon.setInUse(true);
290            // return the JDBC Connection stored in the
291            // PooledConnection object
292            return pcon.getConnection();
293          }
294        }
295    
296        // Could not find a free connection,
297        // create and add a new one
298    
299        // Create a new JDBC Connection
300        Connection con = createConnection();
301    
302        // Create a new PooledConnection, passing it the JDBC
303        // Connection
304        pcon = new PooledConnection(con);
305    
306        // Mark the connection as in use
307        pcon.setInUse(true);
308    
309        // Add the new PooledConnection object to the pool
310        m_pool.addElement(pcon);
311    
312        // return the new Connection
313        return pcon.getConnection();
314      }
315    
316      /**
317       * @param con
318       *
319       * @throws SQLException
320       */
321      public synchronized void releaseConnection( Connection con )throws SQLException
322      {
323    
324        // find the PooledConnection Object
325        for ( int x = 0; x < m_pool.size(); x++ )
326        {
327    
328          PooledConnection pcon =
329            (PooledConnection) m_pool.elementAt(x);
330    
331          // Check for correct Connection
332          if ( pcon.getConnection() == con )
333          {
334            if (DEBUG)
335            {
336              System.out.println("Releasing Connection " + x);
337            }
338    
339            if (! isEnabled())
340            {
341              con.close();
342              m_pool.removeElementAt(x);
343              if (DEBUG)
344              {
345                System.out.println("-->Inactive Pool, Closing connection");
346              }
347    
348            }
349            else
350            {
351              // Set it's inuse attribute to false, which
352              // releases it for use
353              pcon.setInUse(false);
354            }
355    
356            break;
357          }
358        }
359      }
360    
361    
362      /**
363       * @param con
364       *
365       * @throws SQLException
366       */
367      public synchronized void releaseConnectionOnError( Connection con )throws SQLException
368      {
369    
370        // find the PooledConnection Object
371        for ( int x = 0; x < m_pool.size(); x++ )
372        {
373    
374          PooledConnection pcon =
375            (PooledConnection) m_pool.elementAt(x);
376    
377          // Check for correct Connection
378          if ( pcon.getConnection() == con )
379          {
380            if (DEBUG)
381            {
382              System.out.println("Releasing Connection On Error" + x);
383            }
384    
385            con.close();
386            m_pool.removeElementAt(x);
387            if (DEBUG)
388            {
389              System.out.println("-->Inactive Pool, Closing connection");
390            }
391            break;
392          }
393        }
394      }
395    
396    
397      /**
398       *
399       * @throws SQLException
400       */
401      private Connection createConnection( )throws SQLException
402      {
403        Connection con = null;
404    
405        // Create a Connection directly from the Driver that was loaded
406        // with the context class loader. This is to support JDK1.4
407        con = m_Driver.connect(m_url, m_ConnectionProtocol );
408    
409        return con;
410      }
411    
412      // Initialize the pool
413      /**
414       *
415       * @throws IllegalArgumentException
416       * @throws SQLException
417       */
418      public synchronized void initializePool( )throws IllegalArgumentException, SQLException
419      {
420    
421         // Check our initial values
422         if ( m_driver == null )
423         {
424           throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_DRIVER_NAME_SPECIFIED, null));
425           // "No Driver Name Specified!");
426         }
427    
428         if ( m_url == null )
429         {
430           throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_URL_SPECIFIED, null));
431           // "No URL Specified!");
432         }
433    
434         if ( m_PoolMinSize < 1 )
435         {
436           throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_POOLSIZE_LESS_THAN_ONE, null));
437           // "Pool size is less than 1!");
438         }
439    
440         // Create the Connections
441         // Load the Driver class file
442    
443         try
444         {
445            // We have also had problems with drivers unloading
446            // load an instance that will get freed with the class.
447            m_Driver = (Driver) ObjectFactory.newInstance(
448              m_driver, ObjectFactory.findClassLoader(), true);
449    
450            // Register the Driver that was loaded with the Context Classloader
451            // but we will ask for connections directly from the Driver
452            // instance
453            DriverManager.registerDriver(m_Driver);
454         }
455         catch(ObjectFactory.ConfigurationError e)
456         {
457           throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_DRIVER_NAME, null));
458           // "Invalid Driver Name Specified!");
459         }
460         catch(Exception e)
461         {
462           throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_DRIVER_NAME, null));
463         }
464    
465         // IF we are not active, don't actuall build a pool yet
466         // Just set up the driver and periphal items.
467         if ( !m_IsActive) return;
468    
469        // Create Connections based on the size member
470        do
471        {
472    
473          Connection con = createConnection();
474    
475          if ( con != null )
476          {
477    
478            // Create a PooledConnection to encapsulate the
479            // real JDBC Connection
480            PooledConnection pcon = new PooledConnection(con);
481    
482            // Add the Connection the pool.
483            addConnection(pcon);
484    
485            if (DEBUG) System.out.println("Adding DB Connection to the Pool");
486          }
487        }
488        while (m_pool.size() < m_PoolMinSize);
489      }
490    
491      // Adds the PooledConnection to the pool
492      /**
493       * @param value
494       *
495       */
496      private void addConnection( PooledConnection value )
497      {
498        // Add the PooledConnection Object to the vector
499        m_pool.addElement(value);
500      }
501    
502    
503      /**
504       *
505       * @throws Throwable
506       */
507      protected void finalize( )throws Throwable
508      {
509        if (DEBUG)
510        {
511          System.out.println("In Default Connection Pool, Finalize");
512        }
513    
514        // Iterate over the entire pool closing the
515        // JDBC Connections.
516        for ( int x = 0; x < m_pool.size(); x++ )
517        {
518    
519          if (DEBUG)
520          {
521            System.out.println("Closing JDBC Connection " + x);
522          }
523    
524          PooledConnection pcon =
525            (PooledConnection) m_pool.elementAt(x);
526    
527          // If the PooledConnection is not in use, close it
528          if ( pcon.inUse() == false ) { pcon.close();  }
529          else
530          {
531            if (DEBUG)
532            {
533              System.out.println("--> Force close");
534            }
535    
536            // If it still in use, sleep for 30 seconds and
537            // force close.
538            try
539            {
540              java.lang.Thread.sleep(30000);
541              pcon.close();
542            }
543            catch (InterruptedException ie)
544            {
545              if (DEBUG) System.err.println(ie.getMessage());
546            }
547          }
548        }
549    
550        if (DEBUG)
551        {
552          System.out.println("Exit Default Connection Pool, Finalize");
553        }
554    
555        super.finalize();
556      }
557    
558      /**
559       * The Pool can be Enabled and Disabled. Disabling the pool
560       * closes all the outstanding Unused connections and any new
561       * connections will be closed upon release.
562       *
563       * @param flag Control the Connection Pool.
564       * If it is enabled then Connections will actuall be held
565       * around. If disabled then all unused connections will be instantly
566       * closed and as connections are released they are closed and removed
567       * from the pool.
568       *
569       *
570       */
571      public void setPoolEnabled( boolean flag )
572      {
573         m_IsActive = flag;
574         if ( ! flag )
575          freeUnused();
576      }
577    
578    }