Coverage Report - org.apache.ojb.broker.accesslayer.ConnectionFactoryAbstractImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ConnectionFactoryAbstractImpl
N/A
N/A
3.417
 
 1  
 package org.apache.ojb.broker.accesslayer;
 2  
 
 3  
 /* Copyright 2002-2005 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
 19  
 import org.apache.ojb.broker.platforms.PlatformException;
 20  
 import org.apache.ojb.broker.platforms.PlatformFactory;
 21  
 import org.apache.ojb.broker.util.ClassHelper;
 22  
 import org.apache.ojb.broker.util.logging.Logger;
 23  
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 24  
 
 25  
 import javax.naming.InitialContext;
 26  
 import javax.naming.NamingException;
 27  
 import javax.sql.DataSource;
 28  
 import java.sql.Connection;
 29  
 import java.sql.DriverManager;
 30  
 import java.sql.SQLException;
 31  
 import java.util.HashMap;
 32  
 import java.util.Map;
 33  
 import java.util.Properties;
 34  
 
 35  
 /**
 36  
  * Abstract base class to simplify implementation of {@link ConnectionFactory}'s.
 37  
  *
 38  
  * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
 39  
  * @version $Id: ConnectionFactoryAbstractImpl.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
 40  
  */
 41  
 public abstract class ConnectionFactoryAbstractImpl implements ConnectionFactory
 42  
 {
 43  
     private Logger log = LoggerFactory.getLogger(ConnectionFactoryAbstractImpl.class);
 44  
 
 45  
     /**
 46  
      * holds the datasource looked up from JNDI in a map, keyed
 47  
      * by the JNDI name.
 48  
      */
 49  
     private Map dataSourceCache = new HashMap();
 50  
 
 51  
     /**
 52  
      * Returns a valid JDBC Connection. Implement this method in concrete subclasses.
 53  
      * Concrete implementations using Connection pooling are responsible for any validation
 54  
      * and pool removal management.
 55  
      * <p>
 56  
      * Note: This method is never called for a jdbc-connection-descriptor that uses datasources,
 57  
      * OJB only manages connections from DriverManager.
 58  
      * <p>
 59  
      * Note: If the concrete implementation does not callback to
 60  
      * {@link #newConnectionFromDriverManager(org.apache.ojb.broker.metadata.JdbcConnectionDescriptor)}
 61  
      * when creating a new Connection, it <em>must</em> call
 62  
      * {@link #initializeJdbcConnection(java.sql.Connection, org.apache.ojb.broker.metadata.JdbcConnectionDescriptor)}
 63  
      * so that the platform implementation can peform any RDBMS-specific init tasks for newly
 64  
      * created Connection objetcs.
 65  
      *
 66  
      * @param jcd the connection descriptor for which to return a validated Connection
 67  
      * @return a valid Connection, never null.
 68  
      * Specific implementations <em>must</em> guarantee that the connection is not null and
 69  
      * that it is valid.
 70  
      * @throws LookupException if a valid Connection could not be obtained
 71  
      */
 72  
     public abstract Connection checkOutJdbcConnection(JdbcConnectionDescriptor jcd)
 73  
             throws LookupException;
 74  
 
 75  
     /**
 76  
      * Releases a Connection after use. Implement this method in concrete subclasses.
 77  
      * Concrete implementations using Connection pooling are responsible for any validation
 78  
      * and pool removal management.
 79  
      * <p>
 80  
      * Note: This method is never called for a jdbc-connection-descriptor that uses datasources,
 81  
      * OJB only manages connections from DriverManager.
 82  
      *
 83  
      * @param jcd the connection descriptor for which the connection was created
 84  
      * @param con the connection to release.
 85  
      * Callers <em>must</em> guarantee that the passed connection was obtained by calling
 86  
      * {@link #checkOutJdbcConnection(org.apache.ojb.broker.metadata.JdbcConnectionDescriptor)}.
 87  
      * @throws LookupException if errors occured during release of object. Typically happens
 88  
      * if return of object to pool fails in a pooled implementation.
 89  
      */
 90  
     public abstract void releaseJdbcConnection(JdbcConnectionDescriptor jcd, Connection con)
 91  
             throws LookupException;
 92  
 
 93  
     public void releaseConnection(JdbcConnectionDescriptor jcd, Connection con)
 94  
     {
 95  
         if (con == null) return;
 96  
         if (jcd.isDataSource())
 97  
         {
 98  
             try
 99  
             {
 100  
                 con.close();
 101  
             }
 102  
             catch (SQLException e)
 103  
             {
 104  
                 log.error("Closing connection failed", e);
 105  
             }
 106  
         }
 107  
         else
 108  
         {
 109  
             try
 110  
             {
 111  
                 releaseJdbcConnection(jcd, con);
 112  
             }
 113  
             catch (LookupException e)
 114  
             {
 115  
                 log.error("Unexpected exception when return connection " + con +
 116  
                         " to pool using " + jcd, e);
 117  
             }
 118  
         }
 119  
     }
 120  
 
 121  
     public Connection lookupConnection(JdbcConnectionDescriptor jcd) throws LookupException
 122  
     {
 123  
         Connection conn;
 124  
         /*
 125  
         use JNDI datasourcelookup or ordinary jdbc DriverManager
 126  
         to obtain connection ?
 127  
         */
 128  
         if (jcd.isDataSource())
 129  
         {
 130  
             if (log.isDebugEnabled())
 131  
             {
 132  
                 log.debug("do datasource lookup, name: " + jcd.getDatasourceName() +
 133  
                         ", user: " + jcd.getUserName());
 134  
             }
 135  
             conn = newConnectionFromDataSource(jcd);
 136  
         }
 137  
         else
 138  
         {
 139  
             conn = checkOutJdbcConnection(jcd);
 140  
             // connection is now guaranteed to be valid by API contract (else exception is thrown)
 141  
         }
 142  
         return conn;
 143  
     }
 144  
 
 145  
     /**
 146  
      * Initialize the connection with the specified properties in OJB
 147  
      * configuration files and platform depended properties.
 148  
      * Invoke this method after a NEW connection is created, not if re-using from pool.
 149  
      *
 150  
      * @see org.apache.ojb.broker.platforms.PlatformFactory
 151  
      * @see org.apache.ojb.broker.platforms.Platform
 152  
      */
 153  
     protected void initializeJdbcConnection(Connection con, JdbcConnectionDescriptor jcd)
 154  
             throws LookupException
 155  
     {
 156  
         try
 157  
         {
 158  
             PlatformFactory.getPlatformFor(jcd).initializeJdbcConnection(jcd, con);
 159  
         }
 160  
         catch (PlatformException e)
 161  
         {
 162  
             throw new LookupException("Platform dependent initialization of connection failed", e);
 163  
         }
 164  
     }
 165  
 
 166  
     /**
 167  
      * Override this method to do cleanup in your implementation.
 168  
      * Do a <tt>super.releaseAllResources()</tt> in your method implementation
 169  
      * to free resources used by this class.
 170  
      */
 171  
     public synchronized void releaseAllResources()
 172  
     {
 173  
         this.dataSourceCache.clear();
 174  
     }
 175  
 
 176  
     /**
 177  
      * Creates a new connection from the data source that the connection descriptor
 178  
      * represents. If the connection descriptor does not directly contain the data source
 179  
      * then a JNDI lookup is performed to retrieve the data source.
 180  
      * 
 181  
      * @param jcd The connection descriptor
 182  
      * @return A connection instance
 183  
      * @throws LookupException if we can't get a connection from the datasource either due to a
 184  
      *          naming exception, a failed sanity check, or a SQLException.
 185  
      */
 186  
     protected Connection newConnectionFromDataSource(JdbcConnectionDescriptor jcd)
 187  
             throws LookupException
 188  
     {
 189  
         Connection retval = null;
 190  
         // use JNDI lookup
 191  
         DataSource ds = jcd.getDataSource();
 192  
 
 193  
         if (ds == null)
 194  
         {
 195  
             // [tomdz] Would it suffice to store the datasources only at the JCDs ?
 196  
             //         Only possible problem would be serialization of the JCD because
 197  
             //         the data source object in the JCD does not 'survive' this
 198  
             ds = (DataSource) dataSourceCache.get(jcd.getDatasourceName());
 199  
         }
 200  
         try
 201  
         {
 202  
             if (ds == null)
 203  
             {
 204  
                 /**
 205  
                  * this synchronization block won't be a big deal as we only look up
 206  
                  * new datasources not found in the map.
 207  
                  */
 208  
                 synchronized (dataSourceCache)
 209  
                 {
 210  
                     InitialContext ic = new InitialContext();
 211  
                     ds = (DataSource) ic.lookup(jcd.getDatasourceName());
 212  
                     /**
 213  
                      * cache the datasource lookup.
 214  
                      */
 215  
                     dataSourceCache.put(jcd.getDatasourceName(), ds);
 216  
                 }
 217  
             }
 218  
             if (jcd.getUserName() == null)
 219  
             {
 220  
                 retval = ds.getConnection();
 221  
             }
 222  
             else
 223  
             {
 224  
                 retval = ds.getConnection(jcd.getUserName(), jcd.getPassWord());
 225  
             }
 226  
         }
 227  
         catch (SQLException sqlEx)
 228  
         {
 229  
             log.error("SQLException thrown while trying to get Connection from Datasource (" +
 230  
                     jcd.getDatasourceName() + ")", sqlEx);
 231  
             throw new LookupException("SQLException thrown while trying to get Connection from Datasource (" +
 232  
                     jcd.getDatasourceName() + ")", sqlEx);
 233  
         }
 234  
         catch (NamingException namingEx)
 235  
         {
 236  
             log.error("Naming Exception while looking up DataSource (" + jcd.getDatasourceName() + ")", namingEx);
 237  
             throw new LookupException("Naming Exception while looking up DataSource (" + jcd.getDatasourceName() +
 238  
                     ")", namingEx);
 239  
         }
 240  
         // initialize connection
 241  
         initializeJdbcConnection(retval, jcd);
 242  
         if(log.isDebugEnabled()) log.debug("Create new connection using DataSource: "+retval);
 243  
         return retval;
 244  
     }
 245  
 
 246  
     /**
 247  
      * Returns a new created connection
 248  
      *
 249  
      * @param jcd the connection descriptor
 250  
      * @return an instance of Connection from the drivermanager
 251  
      */
 252  
     protected Connection newConnectionFromDriverManager(JdbcConnectionDescriptor jcd)
 253  
             throws LookupException
 254  
     {
 255  
         Connection retval = null;
 256  
         // use JDBC DriverManager
 257  
         final String driver = jcd.getDriver();
 258  
         final String url = getDbURL(jcd);
 259  
         try
 260  
         {
 261  
             // loads the driver - NB call to newInstance() added to force initialisation
 262  
             ClassHelper.getClass(driver, true);
 263  
             final String user = jcd.getUserName();
 264  
             final String password = jcd.getPassWord();
 265  
             final Properties properties = getJdbcProperties(jcd, user, password);
 266  
             if (properties.isEmpty())
 267  
             {
 268  
                 if (user == null)
 269  
                 {
 270  
                     retval = DriverManager.getConnection(url);
 271  
                 }
 272  
                 else
 273  
                 {
 274  
                     retval = DriverManager.getConnection(url, user, password);
 275  
                 }
 276  
             }
 277  
             else
 278  
             {
 279  
                 retval = DriverManager.getConnection(url, properties);
 280  
             }
 281  
         }
 282  
         catch (SQLException sqlEx)
 283  
         {
 284  
             log.error("Error getting Connection from DriverManager with url (" + url + ") and driver (" + driver + ")", sqlEx);
 285  
             throw new LookupException("Error getting Connection from DriverManager with url (" + url + ") and driver (" + driver + ")", sqlEx);
 286  
         }
 287  
         catch (ClassNotFoundException cnfEx)
 288  
         {
 289  
             log.error(cnfEx);
 290  
             throw new LookupException("A class was not found", cnfEx);
 291  
         }
 292  
         catch (Exception e)
 293  
         {
 294  
             log.error("Instantiation of jdbc driver failed", e);
 295  
             throw new LookupException("Instantiation of jdbc driver failed", e);
 296  
         }
 297  
         // initialize connection
 298  
         initializeJdbcConnection(retval, jcd);
 299  
         if(log.isDebugEnabled()) log.debug("Create new connection using DriverManager: "+retval);
 300  
         return retval;
 301  
     }
 302  
 
 303  
     /**
 304  
      * Returns connection properties for passing to DriverManager, after merging
 305  
      * JDBC driver-specific configuration settings with name/password from connection
 306  
      * descriptor.
 307  
      * @param jcd the connection descriptor with driver-specific settings
 308  
      * @param user the jcd username (or null if not using authenticated login)
 309  
      * @param password the jcd password (only used when user != null)
 310  
      * @return merged properties object to pass to DriverManager
 311  
      */
 312  
     protected Properties getJdbcProperties(JdbcConnectionDescriptor jcd,
 313  
                                            String user, String password)
 314  
     {
 315  
         final Properties jdbcProperties;
 316  
         jdbcProperties = jcd.getConnectionPoolDescriptor().getJdbcProperties();
 317  
         if (user != null)
 318  
         {
 319  
             jdbcProperties.put("user", user);
 320  
             jdbcProperties.put("password", password);
 321  
         }
 322  
         return jdbcProperties;
 323  
     }
 324  
 
 325  
     protected Properties getJdbcProperties(JdbcConnectionDescriptor jcd)
 326  
     {
 327  
         final String user = jcd.getUserName();
 328  
         final String password = jcd.getPassWord();
 329  
         return getJdbcProperties(jcd, user, password);
 330  
     }
 331  
 
 332  
     protected String getDbURL(JdbcConnectionDescriptor jcd)
 333  
     {
 334  
         return jcd.isDataSource() ? jcd.getDatasourceName() :
 335  
                 jcd.getProtocol() + ":" + jcd.getSubProtocol() + ":" + jcd.getDbAlias();
 336  
     }
 337  
 
 338  
     protected String getJcdDescription(JdbcConnectionDescriptor jcd)
 339  
     {
 340  
         return "Connection for JdbcConnectionDescriptor (" +
 341  
                (jcd.getDatasourceName() != null ? "datasource: " + jcd.getDatasourceName() :
 342  
                 "db-url: " + getDbURL(jcd) + ", user: " + jcd.getUserName()) +
 343  
                ")";
 344  
     }
 345  
 
 346  
 }