View Javadoc
1 /* ==================================================================== 2 * License: 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * 3. The end-user documentation included with the redistribution, 17 * if any, must include the following acknowledgment: 18 * "This product includes software developed by 19 * Robert Half International (http://www.rhi.com/)." 20 * Alternately, this acknowledgment may appear in the software itself, 21 * if and wherever such third-party acknowledgments normally appear. 22 * 23 * 4. The names "Parc", "RHI", and "Robert Half International" must 24 * not be used to endorse or promote products derived from this 25 * software without prior written permission. For written 26 * permission, please contact pete.mckinstry@rhi.com. 27 * 28 * 5. Products derived from this software may not be called "PARC", 29 * nor may "PARC" appear in their name, without prior written 30 * permission of Robert Half International. 31 * 32 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 33 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 34 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 35 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 36 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 37 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 38 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 39 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 40 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 41 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 42 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 43 * SUCH DAMAGE. 44 * ==================================================================== 45 * 46 */ 47 package com.rhi.architecture.batch; 48 49 50 // rhi classes 51 import com.rhi.architecture.resource.InitializationException; 52 53 import java.sql.*; 54 55 import java.text.MessageFormat; 56 57 // java classes 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.Collections; 61 import java.util.Iterator; 62 import java.util.List; 63 import java.util.Properties; 64 65 66 /*** 67 * Loads configuration information from a database table and adds it to the property information. 68 * 69 * @author <a href="mailto:josh.tolle@rhi.com">Josh Tolle</a> 70 * 71 * @copyright 2002, Robert Half Int'l., Inc. All rights reserved. 72 * @since 1.0 73 */ 74 public class HierarchicalDBConfigMixin 75 implements ConfigMixin { 76 //~ Static fields/initializers ----------------------------------------------------------------- 77 78 /*** 79 * The name of the property that defines SQL query to use when retrieving properties from the 80 * database. 81 */ 82 public static final String SQL_QUERY_PROPERTY_NAME = "propsSqlQuery"; 83 84 /*** 85 * The name of the property that defines the password for the database from which properties 86 * are retrieved. 87 */ 88 public static final String DB_PASSWORD_PROPERTY_NAME = "password"; 89 90 /*** 91 * The name of the property that defines the user for the database from which properties are 92 * retrieved. 93 */ 94 public static final String DB_USER_PROPERTY_NAME = "username"; 95 96 /*** 97 * The name of the property that defines the URL to the database from which properties are 98 * retrieved. 99 */ 100 public static final String DB_URL_PROPERTY_NAME = "db_url"; 101 102 /*** 103 * The name of the property that defines the driver to use when connecting to the database from 104 * which properties are retrieved. 105 */ 106 public static final String DB_DRIVER_PROPERTY_NAME = "driver"; 107 private static final String MISSING_PROPERTY_VALUE = "missing"; 108 109 /*** 110 * The name of the property that defines the name of the application that should be used to 111 * load baseline properties. 112 */ 113 public static final String PARENT_PROPERTY_NAME = "parent.application"; 114 115 /*** 116 * The name of the property that defines the schema in the database in which the properties 117 * table resides. This is used by a call to {@link java.text.MessageFormat MessageFormat} to 118 * replace the <code>{0}</code> in the SQL statement. 119 */ 120 public static final String SCHEMA_PROPERTY_NAME = "HierarchicalDBConfigMixin.schema"; 121 122 /*** 123 * The default column names for retrieving properties from the database. 124 */ 125 public static final String[] DEFAULT_COLUMNS = { "name", "value" }; 126 127 /*** 128 * The SQL statement that is used by default is the {@link #SQL_QUERY_PROPERTY_NAME 129 * SQL_QUERY_PROPERTY_NAME} is not defined. The SQL statement, be it this default one or one 130 * passed in viat the SQL_QUERY_PROPERTY_NAME property, needs to have a <code>{0}</code> prior 131 * to any table names for formatting; if there is a {@link #SCHEMA_PROPERTY_NAME 132 * SCHEMA_PROPERTY_NAME} propery set, this will be replaced with the value therein, otherwise 133 * it is discarded. 134 */ 135 public static final String DEFAULT_SQL = 136 "select name, value from {0}rh_ia_int_prog_config where prog_name = ? and environment = ?"; 137 138 /*** 139 * Separator between heirerarchical properties. 140 */ 141 public static final String PROP_SEPARATOR = "."; 142 143 /*** 144 * The base value for the second bindable value of the SQL statement 145 */ 146 public static final String BASE_ENV = "common"; 147 148 //~ Instance fields ---------------------------------------------------------------------------- 149 150 private String mixinName = this.getClass().getName(); 151 private String sql; 152 153 //~ Methods ------------------------------------------------------------------------------------ 154 155 /*** 156 * Retrieve property values and append them to the provided default application properties. 157 * 158 * <p> 159 * This method also has a recursive ability to load parent properties. It does this by looking 160 * for a property ({@link #PARENT_PROPERTY_NAME PARENT_PROPERTY_NAME}) to be set at the level 161 * at which it is looking; it will do this a far back as there are parent property values set. 162 * The recursion is done by replacing the first bindable value with the value found in {@link 163 * #PARENT_PROPERTY_NAME PARENT_PROPERTY_NAME} each time such a property is found. 164 * </p> 165 * 166 * <p> 167 * If there is no {@link #SQL_QUERY_PROPERTY_NAME SQL_QUERY_PROPERTY_NAME} property found, the 168 * default properties SQL statement is set to {@link #DEFAULT_SQL DEFAULT_SQL}. If a SQL 169 * statement is set, then the names of the columns to get from the result set as the 170 * properties "DatabaseConfigMixin.col0" and quotDatabaseConfigMixin.col1" (if 171 * these are not found, the values default to {@link #DEFAULT_COLUMNS DEFAULT_COLUMNS}), and 172 * the bindable values in the properties "DatabaseConfigMixin.val0" (used as the 173 * heirerarchical key) through "DatabaseConfigMixin.val<i>N</i>". Also, the value in 174 * the {@link #SCHEMA_PROPERTY_NAME SCHEMA_PROPERTY_NAME}, if it exists, will replace any 175 * instances of <code>{0}</code> in any SQL statement passed. 176 * </p> 177 * 178 * <p> 179 * The properties in {@link #DB_DRIVER_PROPERTY_NAME DB_DRIVER_PROPERTY_NAME}, {@link 180 * #DB_PASSWORD_PROPERTY_NAME DB_PASSWORD_PROPERTY_NAME}, {@link #DB_USER_PROPERTY_NAME 181 * DB_USER_PROPERTY_NAME}, and {@link #DB_URL_PROPERTY_NAME DB_URL_PROPERTY_NAME} are all 182 * required for this method to work. 183 * </p> 184 * 185 * @param props - the properties used to access the database 186 * 187 * @throws InitializationException if there is any problem loading properties 188 * 189 * @since 1.1 190 */ 191 public void loadConfiguration(Properties props) 192 throws InitializationException { 193 mixinName = this.getClass().getName(); 194 mixinName = mixinName.substring(mixinName.lastIndexOf(PROP_SEPARATOR) + 1); 195 196 Connection conn = null; 197 String schema = props.getProperty(SCHEMA_PROPERTY_NAME, ""); 198 199 if (schema.length() > 0) { 200 schema = schema + "."; 201 } 202 203 Object[] querySchema = { schema }; 204 this.sql = 205 MessageFormat.format(props.getProperty(SQL_QUERY_PROPERTY_NAME, DEFAULT_SQL), 206 querySchema); 207 208 ArrayList colNames = new ArrayList(); 209 ArrayList bindVals = new ArrayList(); 210 211 if (this.sql.equals(DEFAULT_SQL)) { 212 Properties cols = getDbColumns(props); 213 214 for (int i = 0; true; i++) { 215 String colName = "col" + i; 216 String col = cols.getProperty(colName, MISSING_PROPERTY_VALUE); 217 218 if (col.equals(MISSING_PROPERTY_VALUE)) { 219 break; 220 } 221 else { 222 colNames.add(col); 223 } 224 } 225 226 if (colNames.isEmpty()) { 227 colNames.addAll(Arrays.asList(DEFAULT_COLUMNS)); 228 } 229 } 230 else { 231 colNames.addAll(Arrays.asList(DEFAULT_COLUMNS)); 232 } 233 234 Properties vals = getDbValues(props); 235 236 for (int i = 0; true; i++) { 237 String valName = "val" + i; 238 String val = vals.getProperty(valName, MISSING_PROPERTY_VALUE); 239 240 if (val.equals(MISSING_PROPERTY_VALUE)) { 241 break; 242 } 243 else { 244 bindVals.add(val); 245 } 246 } 247 248 if (bindVals.isEmpty()) { 249 throw new InitializationException("binding values missing from configuration"); 250 } 251 252 // Since nothing is loaded, i can't use the connection pool. 253 // Therefore make the connection explicitly. 254 String driverName = props.getProperty(DB_DRIVER_PROPERTY_NAME, MISSING_PROPERTY_VALUE); 255 256 if (driverName.equals(MISSING_PROPERTY_VALUE)) { 257 throw new InitializationException("driver missing from configuration"); 258 } 259 260 String url = props.getProperty(DB_URL_PROPERTY_NAME, MISSING_PROPERTY_VALUE); 261 262 if (url.equals(MISSING_PROPERTY_VALUE)) { 263 throw new InitializationException("db_url missing from configuration"); 264 } 265 266 String username = props.getProperty(DB_USER_PROPERTY_NAME, MISSING_PROPERTY_VALUE); 267 268 if (username.equals(MISSING_PROPERTY_VALUE)) { 269 throw new InitializationException("username missing from configuration"); 270 } 271 272 String password = props.getProperty(DB_PASSWORD_PROPERTY_NAME, MISSING_PROPERTY_VALUE); 273 274 if (password.equals(MISSING_PROPERTY_VALUE)) { 275 throw new InitializationException("password missing from configuration"); 276 } 277 278 try { 279 // register the driver. 280 Class driverClass = Class.forName(driverName); 281 conn = DriverManager.getConnection(url, username, password); 282 283 Properties recursedProperties = recurseProperties(conn, colNames, bindVals); 284 props.putAll(recursedProperties); 285 } 286 catch (SQLException e) { 287 throw new InitializationException("sql exception loading bootstrap info. e = " + e); 288 } 289 catch (ClassNotFoundException cnfe) { 290 throw new InitializationException("ClassNotFoundException = " + cnfe); 291 } 292 finally { 293 if (conn != null) { 294 try { 295 conn.close(); 296 } 297 catch (SQLException e) { 298 // ignore 299 } 300 } 301 } 302 } 303 304 private Properties getDbColumns(Properties props) { 305 Properties colProps = new Properties(); 306 307 for (Iterator propsIter = Collections.list(props.propertyNames()).iterator(); 308 propsIter.hasNext();) { 309 String prop = (String) propsIter.next(); 310 311 if (prop.startsWith(mixinName + PROP_SEPARATOR + "col")) { 312 colProps.setProperty(prop.substring((prop.indexOf(PROP_SEPARATOR) + 1)), 313 props.getProperty(prop)); 314 } 315 } 316 317 return colProps; 318 } 319 320 private Properties getDbValues(Properties props) { 321 Properties valProps = new Properties(); 322 323 for (Iterator propsIter = Collections.list(props.propertyNames()).iterator(); 324 propsIter.hasNext();) { 325 String prop = (String) propsIter.next(); 326 327 if (prop.startsWith(mixinName + PROP_SEPARATOR + "val")) { 328 valProps.setProperty(prop.substring((prop.indexOf(PROP_SEPARATOR) + 1)), 329 props.getProperty(prop)); 330 } 331 } 332 333 return valProps; 334 } 335 336 private Properties loadProperties(Connection conn, List cols, List vals) 337 throws InitializationException { 338 Properties props = new Properties(); 339 PreparedStatement stmt = null; 340 ResultSet rs = null; 341 342 try { 343 stmt = conn.prepareStatement(this.sql); 344 345 for (int i = 0, n = vals.size(); i < n; i++) { 346 stmt.setObject((i + 1), vals.get(i)); 347 } 348 349 rs = stmt.executeQuery(); 350 351 // add any properties to the list. 352 while (rs.next()) { 353 String name = rs.getString((String) cols.get(0)); 354 String value = rs.getString((String) cols.get(1)); 355 props.setProperty(name, value); 356 } 357 358 return props; 359 } 360 catch (SQLException e) { 361 throw new InitializationException("sql exception loading bootstrap info. e = " + e); 362 } 363 finally { 364 if (rs != null) { 365 try { 366 rs.close(); 367 } 368 catch (SQLException e) { 369 // ignore 370 } 371 } 372 373 if (stmt != null) { 374 try { 375 stmt.close(); 376 } 377 catch (SQLException e) { 378 // ignore 379 } 380 } 381 } 382 383 // finally block 384 } 385 386 private Properties recurseProperties(Connection conn, List colNames, List bindVals) 387 throws InitializationException { 388 // get properties from database 389 Properties resultProps = new Properties(); 390 Properties interfaceProps = new Properties(); 391 Properties parentProps = new Properties(); 392 List baseBinding = new ArrayList(bindVals); 393 baseBinding.set(1, BASE_ENV); 394 interfaceProps.putAll(loadProperties(conn, colNames, baseBinding)); 395 interfaceProps.putAll(loadProperties(conn, colNames, bindVals)); 396 397 String parentProp = 398 interfaceProps.getProperty(PARENT_PROPERTY_NAME, MISSING_PROPERTY_VALUE); 399 400 if (!parentProp.equals(MISSING_PROPERTY_VALUE)) { 401 bindVals.set(0, parentProp); 402 parentProps = recurseProperties(conn, colNames, bindVals); 403 } 404 405 resultProps.putAll(parentProps); 406 resultProps.putAll(interfaceProps); 407 408 return resultProps; 409 } 410 411 // end loadProperties() method. 412 }

This page was automatically generated by Maven