Coverage Report - org.apache.commons.resources.impl.JDBCResources
 
Classes in this File Line Coverage Branch Coverage Complexity
JDBCResources
0%
0/81
0%
0/24
6
 
 1  
 /*
 2  
  * $Id: JDBCResources.java 354539 2005-12-06 20:40:16Z niallp $
 3  
  * $Revision: 354539 $
 4  
  * $Date: 2005-12-06 20:40:16 +0000 (Tue, 06 Dec 2005) $
 5  
  *
 6  
  * ====================================================================
 7  
  *
 8  
  *  Copyright 2003-2005 The Apache Software Foundation
 9  
  *
 10  
  *  Licensed under the Apache License, Version 2.0 (the "License");
 11  
  *  you may not use this file except in compliance with the License.
 12  
  *  You may obtain a copy of the License at
 13  
  *
 14  
  *      http://www.apache.org/licenses/LICENSE-2.0
 15  
  *
 16  
  *  Unless required by applicable law or agreed to in writing, software
 17  
  *  distributed under the License is distributed on an "AS IS" BASIS,
 18  
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 19  
  *  See the License for the specific language governing permissions and
 20  
  *  limitations under the License.
 21  
  *
 22  
  */
 23  
 
 24  
 package org.apache.commons.resources.impl;
 25  
 
 26  
 import java.io.FileNotFoundException;
 27  
 import java.io.IOException;
 28  
 import java.io.InputStream;
 29  
 import java.net.URL;
 30  
 import java.sql.Connection;
 31  
 import java.sql.DriverManager;
 32  
 import java.sql.PreparedStatement;
 33  
 import java.sql.ResultSet;
 34  
 import java.sql.SQLException;
 35  
 import java.util.Locale;
 36  
 import java.util.Map;
 37  
 import java.util.Properties;
 38  
 
 39  
 import org.apache.commons.logging.Log;
 40  
 import org.apache.commons.logging.LogFactory;
 41  
 
 42  
 /**
 43  
  * <p>Concrete implementation of 
 44  
  * {@link org.apache.commons.resources.Resources} that retrieves message
 45  
  * key value pairs using JDBC. For this specific implementation, database
 46  
  * connection properties and <code>Locale</code> for which the document's
 47  
  * messages apply are specified via property files. Resources are looked up in
 48  
  * a hierarchy of database values in a manner identical to that 
 49  
  * performed by <code>java.util.ResourceBundle.getBundle().</code>.
 50  
  * 
 51  
  * </p>
 52  
  *
 53  
  * <p>The base URL passed to our constructor must contain the base name
 54  
  * of a properties file that holds the JDBC datasource configuration.</p>
 55  
  * 
 56  
  * <p>
 57  
  * The expected format of the required properties file might look like this:<br>
 58  
  * <code><pre>
 59  
  * jdbc.connect.driver               = org.gjt.mm.mysql.Driver
 60  
  * jdbc.connect.url                  = jdbc:mysql://localhost/resources
 61  
  * jdbc.connect.login                = resourcesTest
 62  
  * jdbc.connect.password             = resourcesTest
 63  
  * 
 64  
  * jdbc.sql.db                       = resources
 65  
  * jdbc.sql.table                    = resources
 66  
  * jdbc.sql.locale.column            = locale
 67  
  * jdbc.sql.key.column               = msgKey
 68  
  * jdbc.sql.val.column               = val
 69  
  * 
 70  
  * org.apache.commons.resource.CACHE = true
 71  
  * 
 72  
  * </pre></code>
 73  
  * 
 74  
  * </p>
 75  
  *
 76  
  * @author James Mitchell
 77  
  * @version $Revision: 354539 $ $Date: 2005-12-06 20:40:16 +0000 (Tue, 06 Dec 2005) $
 78  
  */
 79  
 public class JDBCResources extends CollectionResourcesBase {
 80  
 
 81  
     /**
 82  
      * <p>The <code>Log</code> instance for this class.</p>
 83  
      */
 84  0
     private transient Log log = LogFactory.getLog(JDBCResources.class);
 85  
 
 86  
     // ----------------------------------------------------------- Constructors
 87  
 
 88  
     /**
 89  
      * <p>Create a new 
 90  
      * {@link org.apache.commons.resources.Resources} instance with the specified
 91  
      * logical name and base resource URL.</p>
 92  
      *
 93  
      * @param name Logical name of the new instance
 94  
      * @param base Base URL of the JDBC configuration properties.
 95  
      */
 96  
     public JDBCResources(String name, String base) {
 97  0
         super(name, base);
 98  0
     }
 99  
 
 100  
 
 101  
     // ------------------------------------------------------ Protected Methods
 102  
 
 103  
 
 104  
     /**
 105  
      * <p>Return a <code>Map</code> containing the name-value mappings for
 106  
      * the specified base URL and requested <code>Locale</code>, if there
 107  
      * are any.  If there are no defined mappings for the specified
 108  
      * <code>Locale</code>, return an empty <code>Map</code> instead.</p>
 109  
      *
 110  
      * <p>Concrete subclasses must override this method to perform the
 111  
      * appropriate lookup.  A typical implementation will construct an
 112  
      * absolute URL based on the specified base URL and <code>Locale</code>,
 113  
      * retrieve the specified resource file (if any), and parse it into
 114  
      * a <code>Map</code> structure.</p>
 115  
      *
 116  
      * <p>Caching of previously retrieved <code>Map</code>s (if any) should
 117  
      * be performed by callers of this method.  Therefore, this method should
 118  
      * always attempt to retrieve the specified resource and load it
 119  
      * appropriately.</p>
 120  
      *
 121  
      * @param baseUrl Base URL of the resource files for this 
 122  
      * {@link org.apache.commons.resources.Resources} instance
 123  
      * @param locale <code>Locale</code> for which name-value mappings
 124  
      *  are requested
 125  
      * @return A name-value Map for the specified URL and locale.
 126  
      */
 127  
     protected Map getLocaleMap(String baseUrl, Locale locale) {
 128  
 
 129  0
         if (getLog().isDebugEnabled()) {
 130  0
             getLog().debug("Loading database configuration'" + locale + "' resources from base '" +
 131  
                     baseUrl + "'");
 132  
         }
 133  
 
 134  0
         Properties props = new Properties();
 135  
         //getLocaleSuffix(locale) + 
 136  0
         String name = baseUrl + ".properties";
 137  0
         InputStream stream = null;
 138  
 
 139  
         try {
 140  
 
 141  
             // Open an input stream to the URL for this locale (if any)
 142  0
             if (getLog().isTraceEnabled()) {
 143  0
                 getLog().trace("Absolute URL is '" + name + "'");
 144  
             }
 145  0
             URL url = new URL(name);
 146  0
             stream = url.openStream();
 147  
 
 148  
             // Parse the input stream and populate the name-value mappings map
 149  0
             if (getLog().isTraceEnabled()) {
 150  0
                 getLog().trace("Parsing input resource");
 151  
             }
 152  0
             props.load(stream);
 153  
 
 154  0
         } catch (FileNotFoundException e) {
 155  
 
 156  
             // Log and swallow this exception
 157  0
             if (getLog().isDebugEnabled()) {
 158  0
                 getLog().debug("No resources for locale '" + locale +
 159  
                           "' from base '" + baseUrl + "'");
 160  
             }
 161  0
             props.clear();
 162  
 
 163  0
         } catch (IOException e) {
 164  
 
 165  0
             getLog().warn("IOException loading locale '" + locale +
 166  
                      "' from base '" + baseUrl + "'", e);
 167  0
             props.clear();
 168  
 
 169  
         } finally {
 170  
 
 171  
             // Close the input stream that was opened earlier
 172  0
             if (stream != null) {
 173  
                 try {
 174  0
                     stream.close();
 175  0
                 } catch (IOException e) {
 176  0
                     getLog().error("Error closing stream.", e);
 177  0
                 }
 178  0
                 stream = null;
 179  
             }
 180  
 
 181  
         }
 182  
 
 183  
         // Return the populated (or empty) properties
 184  0
         Properties properties = new Properties();
 185  
         try {
 186  0
             properties = loadData(locale, props);
 187  
 
 188  0
         } catch (InstantiationException e) {
 189  0
             getLog().warn("InstantiationException: locale= '" + locale +
 190  
                      "' base= '" + baseUrl + "'", e);
 191  0
         } catch (IllegalAccessException e) {
 192  0
             getLog().warn("IllegalAccessException: locale= '" + locale +
 193  
                     "' base= '" + baseUrl + "'", e);
 194  0
         } catch (ClassNotFoundException e) {
 195  0
             getLog().warn("Specified Driver not found, make sure it is on " +
 196  
                     "the classpath: locale= '" + locale +
 197  
                     "' base= '" + baseUrl + "'", e);
 198  0
         } catch (SQLException e) {
 199  0
             getLog().warn("SQLException: locale= '" + locale +
 200  
                     "' base= '" + baseUrl + "'", e);
 201  0
         }
 202  0
         return properties;
 203  
 
 204  
         
 205  
 
 206  
     }
 207  
 
 208  
     /**
 209  
      * @param locale <code>Locale</code> for which name-value mappings
 210  
      *  are requested
 211  
      * @param connectionProps The connection properties used to instantiate
 212  
      * a JDBC connection.
 213  
      */
 214  
     private Properties loadData(Locale locale, Properties connectionProps) 
 215  
         throws InstantiationException, IllegalAccessException, 
 216  
         ClassNotFoundException, SQLException {
 217  
         
 218  0
         String driver = connectionProps.getProperty("jdbc.connect.driver");
 219  0
         String url    = connectionProps.getProperty("jdbc.connect.url");
 220  0
         String user = connectionProps.getProperty("jdbc.connect.login");
 221  0
         String pass = connectionProps.getProperty("jdbc.connect.password");
 222  
         
 223  0
         String table = connectionProps.getProperty("jdbc.sql.table");
 224  0
         String localeColumn = connectionProps.getProperty("jdbc.sql.locale.column");
 225  0
         String keyColumn    = connectionProps.getProperty("jdbc.sql.key.column");
 226  0
         String valColumn    = connectionProps.getProperty("jdbc.sql.val.column");
 227  0
         Properties pairs = new Properties();
 228  
         
 229  0
         Connection con = null;
 230  0
         PreparedStatement stmt = null;
 231  0
         ResultSet rs = null;
 232  
         try {
 233  0
             Class.forName(driver).newInstance();
 234  0
             con = DriverManager.getConnection(url, user, pass);
 235  
 
 236  0
             String query = "SELECT " + keyColumn + ", " + valColumn + 
 237  
                     " FROM " + table + " where " + localeColumn + "= '" + locale + "'";
 238  0
             stmt = con.prepareStatement(query);
 239  0
             rs = stmt.executeQuery();
 240  
 
 241  0
             while (rs.next()) {
 242  0
                 pairs.put(rs.getString(keyColumn), rs.getString(valColumn));
 243  
             }
 244  0
         } finally {
 245  0
             try {
 246  0
                 if (rs != null) {
 247  0
                     rs.close();
 248  
                 }
 249  0
             } catch(Exception e) {
 250  0
                 getLog().warn("Error closing ResultSet: " + e);
 251  0
             }
 252  
             try {
 253  0
                 if (stmt != null) {
 254  0
                     stmt.close();
 255  
                 }
 256  0
             } catch(Exception e) {
 257  0
                 getLog().warn("Error closing Statement: " + e);
 258  0
             }
 259  
             try {
 260  0
                 if (con != null) {
 261  0
                     con.close();
 262  
                 }
 263  0
             } catch(Exception e) {
 264  0
                 getLog().warn("Error closing Connection: " + e);
 265  0
             }
 266  0
         }
 267  
 
 268  0
         return pairs;
 269  
     }
 270  
 
 271  
     /**
 272  
      * Accessor method for Log instance.
 273  
      *
 274  
      * The Log instance variable is transient and
 275  
      * accessing it through this method ensures it
 276  
      * is re-initialized when this instance is
 277  
      * de-serialized.
 278  
      *
 279  
      * @return The Log instance.
 280  
      */
 281  
     private Log getLog() {
 282  0
         if (log == null) {
 283  0
             log =  LogFactory.getLog(JDBCResources.class);
 284  
         }
 285  0
         return log;
 286  
     }
 287  
 }