KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > util > SqlResources


1 /***********************************************************************
2  * Copyright (c) 2000-2004 The Apache Software Foundation. *
3  * All rights reserved. *
4  * ------------------------------------------------------------------- *
5  * Licensed under the Apache License, Version 2.0 (the "License"); you *
6  * may not use this file except in compliance with the License. You *
7  * 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 *
14  * implied. See the License for the specific language governing *
15  * permissions and limitations under the License. *
16  ***********************************************************************/

17
18 package org.apache.james.util;
19
20 import org.apache.oro.text.perl.MalformedPerl5PatternException;
21 import org.apache.oro.text.perl.Perl5Util;
22 import org.w3c.dom.*;
23
24 import javax.xml.parsers.DocumentBuilder JavaDoc;
25 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
26 import java.io.File JavaDoc;
27 import java.sql.Connection JavaDoc;
28 import java.sql.SQLException JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.Map JavaDoc;
32
33
34 /**
35  * Provides a set of SQL String resources (eg SQL Strings)
36  * to use for a database connection.
37  * This class allows SQL strings to be customised to particular
38  * database products, by detecting product information from the
39  * jdbc DatabaseMetaData object.
40  *
41  */

42 public class SqlResources
43 {
44     /**
45      * A map of statement types to SQL statements
46      */

47     private Map JavaDoc m_sql = new HashMap JavaDoc();
48
49     /**
50      * A map of engine specific options
51      */

52     private Map JavaDoc m_dbOptions = new HashMap JavaDoc();
53
54     /**
55      * A set of all used String values
56      */

57     static private Map JavaDoc stringTable = java.util.Collections.synchronizedMap(new HashMap JavaDoc());
58
59     /**
60      * A Perl5 regexp matching helper class
61      */

62     private Perl5Util m_perl5Util = new Perl5Util();
63
64     /**
65      * Configures a DbResources object to provide SQL statements from a file.
66      *
67      * SQL statements returned may be specific to the particular type
68      * and version of the connected database, as well as the database driver.
69      *
70      * Parameters encoded as $(parameter} in the input file are
71      * replace by values from the parameters Map, if the named parameter exists.
72      * Parameter values may also be specified in the resourceSection element.
73      *
74      * @param sqlFile the input file containing the string definitions
75      * @param sqlDefsSection
76      * the xml element containing the strings to be used
77      * @param conn the Jdbc DatabaseMetaData, taken from a database connection
78      * @param configParameters a map of parameters (name-value string pairs) which are
79      * replaced where found in the input strings
80      */

81     public void init(File JavaDoc sqlFile, String JavaDoc sqlDefsSection,
82                      Connection JavaDoc conn, Map JavaDoc configParameters)
83         throws Exception JavaDoc
84     {
85         // Parse the sqlFile as an XML document.
86
DocumentBuilderFactory JavaDoc factory = DocumentBuilderFactory.newInstance();
87         DocumentBuilder JavaDoc builder = factory.newDocumentBuilder();
88         Document sqlDoc = builder.parse(sqlFile);
89
90         // First process the database matcher, to determine the
91
// sql statements to use.
92
Element dbMatcherElement =
93             (Element)(sqlDoc.getElementsByTagName("dbMatchers").item(0));
94         String JavaDoc dbProduct = null;
95         if ( dbMatcherElement != null ) {
96             dbProduct = matchDbConnection(conn, dbMatcherElement);
97             m_perl5Util = null; // release the PERL matcher!
98
}
99
100         // Now get the options valid for the database product used.
101
Element dbOptionsElement =
102             (Element)(sqlDoc.getElementsByTagName("dbOptions").item(0));
103         if ( dbOptionsElement != null ) {
104             // First populate the map with default values
105
populateDbOptions("", dbOptionsElement, m_dbOptions);
106             // Now update the map with specific product values
107
if ( dbProduct != null ) {
108                 populateDbOptions(dbProduct, dbOptionsElement, m_dbOptions);
109             }
110         }
111
112         
113         // Now get the section defining sql for the repository required.
114
NodeList sections = sqlDoc.getElementsByTagName("sqlDefs");
115         int sectionsCount = sections.getLength();
116         Element sectionElement = null;
117         for (int i = 0; i < sectionsCount; i++ ) {
118             sectionElement = (Element)(sections.item(i));
119             String JavaDoc sectionName = sectionElement.getAttribute("name");
120             if ( sectionName != null && sectionName.equals(sqlDefsSection) ) {
121                 break;
122             }
123
124         }
125         if ( sectionElement == null ) {
126             StringBuffer JavaDoc exceptionBuffer =
127                 new StringBuffer JavaDoc(64)
128                         .append("Error loading sql definition file. ")
129                         .append("The element named \'")
130                         .append(sqlDefsSection)
131                         .append("\' does not exist.");
132             throw new RuntimeException JavaDoc(exceptionBuffer.toString());
133         }
134
135         // Get parameters defined within the file as defaults,
136
// and use supplied parameters as overrides.
137
Map JavaDoc parameters = new HashMap JavaDoc();
138         // First read from the <params> element, if it exists.
139
Element parametersElement =
140             (Element)(sectionElement.getElementsByTagName("parameters").item(0));
141         if ( parametersElement != null ) {
142             NamedNodeMap params = parametersElement.getAttributes();
143             int paramCount = params.getLength();
144             for (int i = 0; i < paramCount; i++ ) {
145                 Attr param = (Attr)params.item(i);
146                 String JavaDoc paramName = param.getName();
147                 String JavaDoc paramValue = param.getValue();
148                 parameters.put(paramName, paramValue);
149             }
150         }
151         // Then copy in the parameters supplied with the call.
152
parameters.putAll(configParameters);
153
154         // 2 maps - one for storing default statements,
155
// the other for statements with a "db" attribute matching this
156
// connection.
157
Map JavaDoc defaultSqlStatements = new HashMap JavaDoc();
158         Map JavaDoc dbProductSqlStatements = new HashMap JavaDoc();
159
160         // Process each sql statement, replacing string parameters,
161
// and adding to the appropriate map..
162
NodeList sqlDefs = sectionElement.getElementsByTagName("sql");
163         int sqlCount = sqlDefs.getLength();
164         for ( int i = 0; i < sqlCount; i++ ) {
165             // See if this needs to be processed (is default or product specific)
166
Element sqlElement = (Element)(sqlDefs.item(i));
167             String JavaDoc sqlDb = sqlElement.getAttribute("db");
168             Map JavaDoc sqlMap;
169             if ( sqlDb.equals("")) {
170                 // default
171
sqlMap = defaultSqlStatements;
172             }
173             else if (sqlDb.equals(dbProduct) ) {
174                 // Specific to this product
175
sqlMap = dbProductSqlStatements;
176             }
177             else {
178                 // for a different product
179
continue;
180             }
181
182             // Get the key and value for this SQL statement.
183
String JavaDoc sqlKey = sqlElement.getAttribute("name");
184             if ( sqlKey == null ) {
185                 // ignore statements without a "name" attribute.
186
continue;
187             }
188             String JavaDoc sqlString = sqlElement.getFirstChild().getNodeValue();
189
190             // Do parameter replacements for this sql string.
191
Iterator JavaDoc paramNames = parameters.keySet().iterator();
192             while ( paramNames.hasNext() ) {
193                 String JavaDoc paramName = (String JavaDoc)paramNames.next();
194                 String JavaDoc paramValue = (String JavaDoc)parameters.get(paramName);
195
196                 StringBuffer JavaDoc replaceBuffer =
197                     new StringBuffer JavaDoc(64)
198                             .append("${")
199                             .append(paramName)
200                             .append("}");
201                 sqlString = substituteSubString(sqlString, replaceBuffer.toString(), paramValue);
202             }
203
204             // See if we already have registered a string of this value
205
String JavaDoc shared = (String JavaDoc) stringTable.get(sqlString);
206             // If not, register it -- we will use it next time
207
if (shared == null) {
208                 stringTable.put(sqlString, sqlString);
209             } else {
210                 sqlString = shared;
211             }
212
213             // Add to the sqlMap - either the "default" or the "product" map
214
sqlMap.put(sqlKey, sqlString);
215         }
216
217         // Copy in default strings, then overwrite product-specific ones.
218
m_sql.putAll(defaultSqlStatements);
219         m_sql.putAll(dbProductSqlStatements);
220     }
221
222     /**
223      * Compares the DatabaseProductName value for a jdbc Connection
224      * against a set of regular expressions defined in XML.
225      * The first successful match defines the name of the database product
226      * connected to. This value is then used to choose the specific SQL
227      * expressions to use.
228      *
229      * @param conn the JDBC connection being tested
230      * @param dbMatchersElement the XML element containing the database type information
231      *
232      * @return the type of database to which James is connected
233      *
234      */

235     private String JavaDoc matchDbConnection(Connection JavaDoc conn,
236                                      Element dbMatchersElement)
237         throws MalformedPerl5PatternException, SQLException JavaDoc
238     {
239         String JavaDoc dbProductName = conn.getMetaData().getDatabaseProductName();
240     
241         NodeList dbMatchers =
242             dbMatchersElement.getElementsByTagName("dbMatcher");
243         for ( int i = 0; i < dbMatchers.getLength(); i++ ) {
244             // Get the values for this matcher element.
245
Element dbMatcher = (Element)dbMatchers.item(i);
246             String JavaDoc dbMatchName = dbMatcher.getAttribute("db");
247             StringBuffer JavaDoc dbProductPatternBuffer =
248                 new StringBuffer JavaDoc(64)
249                         .append("/")
250                         .append(dbMatcher.getAttribute("databaseProductName"))
251                         .append("/i");
252
253             // If the connection databaseProcuctName matches the pattern,
254
// use the match name from this matcher.
255
if ( m_perl5Util.match(dbProductPatternBuffer.toString(), dbProductName) ) {
256                 return dbMatchName;
257             }
258         }
259         return null;
260     }
261
262     /**
263      * Gets all the name/value pair db option couples related to the dbProduct,
264      * and put them into the dbOptionsMap.
265      *
266      * @param dbProduct the db product used
267      * @param dbOptionsElement the XML element containing the options
268      * @param dbOptionsMap the <CODE>Map</CODE> to populate
269      *
270      */

271     private void populateDbOptions(String JavaDoc dbProduct, Element dbOptionsElement, Map JavaDoc dbOptionsMap)
272     {
273         NodeList dbOptions =
274             dbOptionsElement.getElementsByTagName("dbOption");
275         for ( int i = 0; i < dbOptions.getLength(); i++ ) {
276             // Get the values for this option element.
277
Element dbOption = (Element)dbOptions.item(i);
278             // Check is this element is pertinent to the dbProduct
279
// Notice that a missing attribute returns "", good for defaults
280
if (!dbProduct.equalsIgnoreCase(dbOption.getAttribute("db"))) {
281                 continue;
282             }
283             // Put into the map
284
dbOptionsMap.put(dbOption.getAttribute("name"), dbOption.getAttribute("value"));
285         }
286     }
287
288     /**
289      * Replace substrings of one string with another string and return altered string.
290      * @param input input string
291      * @param find the string to replace
292      * @param replace the string to replace with
293      * @return the substituted string
294      */

295     private String JavaDoc substituteSubString( String JavaDoc input,
296                                         String JavaDoc find,
297                                         String JavaDoc replace )
298     {
299         int find_length = find.length();
300         int replace_length = replace.length();
301
302         StringBuffer JavaDoc output = new StringBuffer JavaDoc(input);
303         int index = input.indexOf(find);
304         int outputOffset = 0;
305
306         while ( index > -1 ) {
307             output.replace(index + outputOffset, index + outputOffset + find_length, replace);
308             outputOffset = outputOffset + (replace_length - find_length);
309
310             index = input.indexOf(find, index + find_length);
311         }
312
313         String JavaDoc result = output.toString();
314         return result;
315     }
316
317     /**
318      * Returns a named SQL string for the specified connection,
319      * replacing parameters with the values set.
320      *
321      * @param name the name of the SQL resource required.
322      * @return the requested resource
323      */

324     public String JavaDoc getSqlString(String JavaDoc name)
325     {
326         return (String JavaDoc)m_sql.get(name);
327     }
328
329     /**
330      * Returns a named SQL string for the specified connection,
331      * replacing parameters with the values set.
332      *
333      * @param name the name of the SQL resource required.
334      * @param required true if the resource is required
335      * @return the requested resource
336      * @throws ConfigurationException
337      * if a required resource cannot be found.
338      */

339     public String JavaDoc getSqlString(String JavaDoc name, boolean required)
340     {
341         String JavaDoc sql = getSqlString(name);
342
343         if (sql == null && required) {
344             StringBuffer JavaDoc exceptionBuffer =
345                 new StringBuffer JavaDoc(64)
346                         .append("Required SQL resource: '")
347                         .append(name)
348                         .append("' was not found.");
349             throw new RuntimeException JavaDoc(exceptionBuffer.toString());
350         }
351         return sql;
352     }
353     
354     /**
355      * Returns the dbOption string value set for the specified dbOption name.
356      *
357      * @param name the name of the dbOption required.
358      * @return the requested dbOption value
359      */

360     public String JavaDoc getDbOption(String JavaDoc name)
361     {
362         return (String JavaDoc)m_dbOptions.get(name);
363     }
364
365 }
366
Popular Tags