View Javadoc
1 /* 2 * Copyright (C) The Apache Software Foundation. All rights reserved. 3 * 4 * This software is published under the terms of the Apache Software License 5 * version 1.1, a copy of which has been included with this distribution in 6 * the LICENSE file. 7 */ 8 package org.apache.james.util; 9 10 import org.apache.oro.text.perl.MalformedPerl5PatternException; 11 import org.apache.oro.text.perl.Perl5Util; 12 import org.w3c.dom.*; 13 14 import javax.xml.parsers.DocumentBuilder; 15 import javax.xml.parsers.DocumentBuilderFactory; 16 import java.io.File; 17 import java.sql.Connection; 18 import java.sql.SQLException; 19 import java.util.HashMap; 20 import java.util.Iterator; 21 import java.util.Map; 22 23 24 /*** 25 * Provides a set of SQL String resources (eg SQL Strings) 26 * to use for a database connection. 27 * This class allows SQL strings to be customised to particular 28 * database products, by detecting product information from the 29 * jdbc DatabaseMetaData object. 30 * 31 * @author Darrell DeBoer <dd@bigdaz.com> 32 */ 33 public class SqlResources 34 { 35 /*** 36 * A map of statement types to SQL statements 37 */ 38 private Map m_sql = new HashMap(); 39 40 /*** 41 * A Perl5 regexp matching helper class 42 */ 43 private Perl5Util m_perl5Util = new Perl5Util(); 44 45 /*** 46 * Configures a DbResources object to provide SQL statements from a file. 47 * 48 * SQL statements returned may be specific to the particular type 49 * and version of the connected database, as well as the database driver. 50 * 51 * Parameters encoded as $(parameter} in the input file are 52 * replace by values from the parameters Map, if the named parameter exists. 53 * Parameter values may also be specified in the resourceSection element. 54 * 55 * @param sqlFile the input file containing the string definitions 56 * @param sqlDefsSection 57 * the xml element containing the strings to be used 58 * @param conn the Jdbc DatabaseMetaData, taken from a database connection 59 * @param configParameters a map of parameters (name-value string pairs) which are 60 * replaced where found in the input strings 61 */ 62 public void init(File sqlFile, String sqlDefsSection, 63 Connection conn, Map configParameters) 64 throws Exception 65 { 66 // Parse the sqlFile as an XML document. 67 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 68 DocumentBuilder builder = factory.newDocumentBuilder(); 69 Document sqlDoc = builder.parse(sqlFile); 70 71 // First process the database matcher, to determine the 72 // sql statements to use. 73 Element dbMatcherElement = 74 (Element)(sqlDoc.getElementsByTagName("dbMatchers").item(0)); 75 String dbProduct = null; 76 if ( dbMatcherElement != null ) { 77 dbProduct = matchDbConnection(conn, dbMatcherElement); 78 } 79 80 // Now get the section defining sql for the repository required. 81 NodeList sections = sqlDoc.getElementsByTagName("sqlDefs"); 82 int sectionsCount = sections.getLength(); 83 Element sectionElement = null; 84 for (int i = 0; i < sectionsCount; i++ ) { 85 sectionElement = (Element)(sections.item(i)); 86 String sectionName = sectionElement.getAttribute("name"); 87 if ( sectionName != null && sectionName.equals(sqlDefsSection) ) { 88 break; 89 } 90 91 } 92 if ( sectionElement == null ) { 93 StringBuffer exceptionBuffer = 94 new StringBuffer(64) 95 .append("Error loading sql definition file. ") 96 .append("The element named \'") 97 .append(sqlDefsSection) 98 .append("\' does not exist."); 99 throw new RuntimeException(exceptionBuffer.toString()); 100 } 101 102 // Get parameters defined within the file as defaults, 103 // and use supplied parameters as overrides. 104 Map parameters = new HashMap(); 105 // First read from the <params> element, if it exists. 106 Element parametersElement = 107 (Element)(sectionElement.getElementsByTagName("parameters").item(0)); 108 if ( parametersElement != null ) { 109 NamedNodeMap params = parametersElement.getAttributes(); 110 int paramCount = params.getLength(); 111 for (int i = 0; i < paramCount; i++ ) { 112 Attr param = (Attr)params.item(i); 113 String paramName = param.getName(); 114 String paramValue = param.getValue(); 115 parameters.put(paramName, paramValue); 116 } 117 } 118 // Then copy in the parameters supplied with the call. 119 parameters.putAll(configParameters); 120 121 // 2 maps - one for storing default statements, 122 // the other for statements with a "db" attribute matching this 123 // connection. 124 Map defaultSqlStatements = new HashMap(); 125 Map dbProductSqlStatements = new HashMap(); 126 127 // Process each sql statement, replacing string parameters, 128 // and adding to the appropriate map.. 129 NodeList sqlDefs = sectionElement.getElementsByTagName("sql"); 130 int sqlCount = sqlDefs.getLength(); 131 for ( int i = 0; i < sqlCount; i++ ) { 132 // See if this needs to be processed (is default or product specific) 133 Element sqlElement = (Element)(sqlDefs.item(i)); 134 String sqlDb = sqlElement.getAttribute("db"); 135 Map sqlMap; 136 if ( sqlDb.equals("")) { 137 // default 138 sqlMap = defaultSqlStatements; 139 } 140 else if (sqlDb.equals(dbProduct) ) { 141 // Specific to this product 142 sqlMap = dbProductSqlStatements; 143 } 144 else { 145 // for a different product 146 continue; 147 } 148 149 // Get the key and value for this SQL statement. 150 String sqlKey = sqlElement.getAttribute("name"); 151 if ( sqlKey == null ) { 152 // ignore statements without a "name" attribute. 153 continue; 154 } 155 String sqlString = sqlElement.getFirstChild().getNodeValue(); 156 157 // Do parameter replacements for this sql string. 158 Iterator paramNames = parameters.keySet().iterator(); 159 while ( paramNames.hasNext() ) { 160 String paramName = (String)paramNames.next(); 161 String paramValue = (String)parameters.get(paramName); 162 163 StringBuffer replaceBuffer = 164 new StringBuffer(64) 165 .append("${") 166 .append(paramName) 167 .append("}"); 168 sqlString = substituteSubString(sqlString, replaceBuffer.toString(), paramValue); 169 } 170 171 // Add to the sqlMap - either the "default" or the "product" map 172 sqlMap.put(sqlKey, sqlString); 173 } 174 175 // Copy in default strings, then overwrite product-specific ones. 176 m_sql.putAll(defaultSqlStatements); 177 m_sql.putAll(dbProductSqlStatements); 178 } 179 180 /*** 181 * Compares the DatabaseProductName value for a jdbc Connection 182 * against a set of regular expressions defined in XML. 183 * The first successful match defines the name of the database product 184 * connected to. This value is then used to choose the specific SQL 185 * expressions to use. 186 * 187 * @param conn the JDBC connection being tested 188 * @param dbMatchersElement the XML element containing the database type information 189 * 190 * @return the type of database to which James is connected 191 * 192 */ 193 private String matchDbConnection(Connection conn, 194 Element dbMatchersElement) 195 throws MalformedPerl5PatternException, SQLException 196 { 197 String dbProductName = conn.getMetaData().getDatabaseProductName(); 198 199 NodeList dbMatchers = 200 dbMatchersElement.getElementsByTagName("dbMatcher"); 201 for ( int i = 0; i < dbMatchers.getLength(); i++ ) { 202 // Get the values for this matcher element. 203 Element dbMatcher = (Element)dbMatchers.item(i); 204 String dbMatchName = dbMatcher.getAttribute("db"); 205 StringBuffer dbProductPatternBuffer = 206 new StringBuffer(64) 207 .append("/") 208 .append(dbMatcher.getAttribute("databaseProductName")) 209 .append("/i"); 210 211 // If the connection databaseProcuctName matches the pattern, 212 // use the match name from this matcher. 213 if ( m_perl5Util.match(dbProductPatternBuffer.toString(), dbProductName) ) { 214 return dbMatchName; 215 } 216 } 217 return null; 218 } 219 220 /*** 221 * Replace substrings of one string with another string and return altered string. 222 * @param input input string 223 * @param find the string to replace 224 * @param replace the string to replace with 225 * @return the substituted string 226 */ 227 private String substituteSubString( String input, 228 String find, 229 String replace ) 230 { 231 int find_length = find.length(); 232 int replace_length = replace.length(); 233 234 StringBuffer output = new StringBuffer(input); 235 int index = input.indexOf(find); 236 int outputOffset = 0; 237 238 while ( index > -1 ) { 239 output.replace(index + outputOffset, index + outputOffset + find_length, replace); 240 outputOffset = outputOffset + (replace_length - find_length); 241 242 index = input.indexOf(find, index + find_length); 243 } 244 245 String result = output.toString(); 246 return result; 247 } 248 249 /*** 250 * Returns a named SQL string for the specified connection, 251 * replacing parameters with the values set. 252 * 253 * @param name the name of the SQL resource required. 254 * @return the requested resource 255 */ 256 public String getSqlString(String name) 257 { 258 return (String)m_sql.get(name); 259 } 260 261 /*** 262 * Returns a named SQL string for the specified connection, 263 * replacing parameters with the values set. 264 * 265 * @param name the name of the SQL resource required. 266 * @param required true if the resource is required 267 * @return the requested resource 268 * @throws ConfigurationException 269 * if a required resource cannot be found. 270 */ 271 public String getSqlString(String name, boolean required) 272 { 273 String sql = getSqlString(name); 274 275 if ( sql == null ) { 276 StringBuffer exceptionBuffer = 277 new StringBuffer(64) 278 .append("Required SQL resource: '") 279 .append(name) 280 .append("' was not found."); 281 throw new RuntimeException(exceptionBuffer.toString()); 282 } 283 return sql; 284 } 285 }

This page was automatically generated by Maven