1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.james.util.sql;
22
23 import org.apache.oro.text.perl.MalformedPerl5PatternException;
24 import org.apache.oro.text.perl.Perl5Util;
25 import org.w3c.dom.Attr;
26 import org.w3c.dom.Document;
27 import org.w3c.dom.Element;
28 import org.w3c.dom.NamedNodeMap;
29 import org.w3c.dom.NodeList;
30
31 import javax.xml.parsers.DocumentBuilder;
32 import javax.xml.parsers.DocumentBuilderFactory;
33 import java.io.File;
34 import java.io.InputStream;
35 import java.sql.Connection;
36 import java.sql.SQLException;
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.Map;
40
41
42
43
44
45
46
47
48
49
50 public class SqlResources
51 {
52
53
54
55 private Map m_sql = new HashMap();
56
57
58
59
60 private Map m_dbOptions = new HashMap();
61
62
63
64
65 static private Map stringTable = java.util.Collections.synchronizedMap(new HashMap());
66
67
68
69
70 private Perl5Util m_perl5Util = new Perl5Util();
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 public void init(File sqlFile, String sqlDefsSection,
90 Connection conn, Map configParameters)
91 throws Exception
92 {
93
94 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
95 DocumentBuilder builder = factory.newDocumentBuilder();
96 Document sqlDoc = builder.parse(sqlFile);
97
98 init(sqlDoc, sqlDefsSection, conn, configParameters);
99 }
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 public void init(InputStream input, String sqlDefsSection,
119 Connection conn, Map configParameters)
120 throws Exception
121 {
122
123 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
124 DocumentBuilder builder = factory.newDocumentBuilder();
125 Document sqlDoc = builder.parse(input);
126
127 init(sqlDoc, sqlDefsSection, conn, configParameters);
128 }
129
130
131
132
133
134
135
136
137
138
139
140 protected void init(Document sqlDoc, String sqlDefsSection,
141 Connection conn, Map configParameters) throws SQLException {
142
143
144 Element dbMatcherElement =
145 (Element)(sqlDoc.getElementsByTagName("dbMatchers").item(0));
146 String dbProduct = null;
147 if ( dbMatcherElement != null ) {
148 dbProduct = matchDbConnection(conn, dbMatcherElement);
149 m_perl5Util = null;
150 }
151
152
153 Element dbOptionsElement =
154 (Element)(sqlDoc.getElementsByTagName("dbOptions").item(0));
155 if ( dbOptionsElement != null ) {
156
157 populateDbOptions("", dbOptionsElement, m_dbOptions);
158
159 if ( dbProduct != null ) {
160 populateDbOptions(dbProduct, dbOptionsElement, m_dbOptions);
161 }
162 }
163
164
165
166 NodeList sections = sqlDoc.getElementsByTagName("sqlDefs");
167 int sectionsCount = sections.getLength();
168 Element sectionElement = null;
169 boolean found = false;
170 for (int i = 0; i < sectionsCount; i++ ) {
171 sectionElement = (Element)(sections.item(i));
172 String sectionName = sectionElement.getAttribute("name");
173 if ( sectionName != null && sectionName.equals(sqlDefsSection) ) {
174 found = true;
175 break;
176 }
177
178 }
179 if ( !found ) {
180 StringBuffer exceptionBuffer =
181 new StringBuffer(64)
182 .append("Error loading sql definition file. ")
183 .append("The element named \'")
184 .append(sqlDefsSection)
185 .append("\' does not exist.");
186 throw new RuntimeException(exceptionBuffer.toString());
187 }
188
189
190
191 Map parameters = new HashMap();
192
193 Element parametersElement =
194 (Element)(sectionElement.getElementsByTagName("parameters").item(0));
195 if ( parametersElement != null ) {
196 NamedNodeMap params = parametersElement.getAttributes();
197 int paramCount = params.getLength();
198 for (int i = 0; i < paramCount; i++ ) {
199 Attr param = (Attr)params.item(i);
200 String paramName = param.getName();
201 String paramValue = param.getValue();
202 parameters.put(paramName, paramValue);
203 }
204 }
205
206 parameters.putAll(configParameters);
207
208
209
210
211 Map defaultSqlStatements = new HashMap();
212 Map dbProductSqlStatements = new HashMap();
213
214
215
216 NodeList sqlDefs = sectionElement.getElementsByTagName("sql");
217 int sqlCount = sqlDefs.getLength();
218 for ( int i = 0; i < sqlCount; i++ ) {
219
220 Element sqlElement = (Element)(sqlDefs.item(i));
221 String sqlDb = sqlElement.getAttribute("db");
222 Map sqlMap;
223 if ( sqlDb.equals("")) {
224
225 sqlMap = defaultSqlStatements;
226 }
227 else if (sqlDb.equals(dbProduct) ) {
228
229 sqlMap = dbProductSqlStatements;
230 }
231 else {
232
233 continue;
234 }
235
236
237 String sqlKey = sqlElement.getAttribute("name");
238 if ( sqlKey == null ) {
239
240 continue;
241 }
242 String sqlString = sqlElement.getFirstChild().getNodeValue();
243
244
245 Iterator paramNames = parameters.keySet().iterator();
246 StringBuffer replaceBuffer = new StringBuffer(64);
247 while ( paramNames.hasNext() ) {
248 String paramName = (String)paramNames.next();
249 String paramValue = (String)parameters.get(paramName);
250 replaceBuffer.append("${").append(paramName).append("}");
251 sqlString = substituteSubString(sqlString, replaceBuffer.toString(), paramValue);
252 if (paramNames.hasNext()) replaceBuffer.setLength(0);
253 }
254
255
256 String shared = (String) stringTable.get(sqlString);
257
258 if (shared == null) {
259 stringTable.put(sqlString, sqlString);
260 } else {
261 sqlString = shared;
262 }
263
264
265 sqlMap.put(sqlKey, sqlString);
266 }
267
268
269 m_sql.putAll(defaultSqlStatements);
270 m_sql.putAll(dbProductSqlStatements);
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286 private String matchDbConnection(Connection conn,
287 Element dbMatchersElement)
288 throws MalformedPerl5PatternException, SQLException
289 {
290 String dbProductName = conn.getMetaData().getDatabaseProductName();
291
292 NodeList dbMatchers =
293 dbMatchersElement.getElementsByTagName("dbMatcher");
294 for ( int i = 0; i < dbMatchers.getLength(); i++ ) {
295
296 Element dbMatcher = (Element)dbMatchers.item(i);
297 String dbMatchName = dbMatcher.getAttribute("db");
298 StringBuffer dbProductPatternBuffer =
299 new StringBuffer(64)
300 .append("/")
301 .append(dbMatcher.getAttribute("databaseProductName"))
302 .append("/i");
303
304
305
306 if ( m_perl5Util.match(dbProductPatternBuffer.toString(), dbProductName) ) {
307 return dbMatchName;
308 }
309 }
310 return null;
311 }
312
313
314
315
316
317
318
319
320
321
322 private void populateDbOptions(String dbProduct, Element dbOptionsElement, Map dbOptionsMap)
323 {
324 NodeList dbOptions =
325 dbOptionsElement.getElementsByTagName("dbOption");
326 for ( int i = 0; i < dbOptions.getLength(); i++ ) {
327
328 Element dbOption = (Element)dbOptions.item(i);
329
330
331 if (!dbProduct.equalsIgnoreCase(dbOption.getAttribute("db"))) {
332 continue;
333 }
334
335 dbOptionsMap.put(dbOption.getAttribute("name"), dbOption.getAttribute("value"));
336 }
337 }
338
339
340
341
342
343
344
345
346 private String substituteSubString( String input,
347 String find,
348 String replace )
349 {
350 int find_length = find.length();
351 int replace_length = replace.length();
352
353 StringBuffer output = new StringBuffer(input);
354 int index = input.indexOf(find);
355 int outputOffset = 0;
356
357 while ( index > -1 ) {
358 output.replace(index + outputOffset, index + outputOffset + find_length, replace);
359 outputOffset = outputOffset + (replace_length - find_length);
360
361 index = input.indexOf(find, index + find_length);
362 }
363
364 String result = output.toString();
365 return result;
366 }
367
368
369
370
371
372
373
374
375 public String getSqlString(String name)
376 {
377 return (String)m_sql.get(name);
378 }
379
380
381
382
383
384
385
386
387
388
389 public String getSqlString(String name, boolean required)
390 {
391 String sql = getSqlString(name);
392
393 if (sql == null && required) {
394 StringBuffer exceptionBuffer =
395 new StringBuffer(64)
396 .append("Required SQL resource: '")
397 .append(name)
398 .append("' was not found.");
399 throw new RuntimeException(exceptionBuffer.toString());
400 }
401 return sql;
402 }
403
404
405
406
407
408
409
410 public String getDbOption(String name)
411 {
412 return (String)m_dbOptions.get(name);
413 }
414
415 }