KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > jdbc > support > SQLErrorCodesFactory


1 /*
2  * Copyright 2002-2006 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.jdbc.support;
18
19 import java.util.Collections JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.Map JavaDoc;
23
24 import javax.sql.DataSource JavaDoc;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 import org.springframework.beans.BeansException;
30 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
31 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
32 import org.springframework.core.io.ClassPathResource;
33 import org.springframework.core.io.Resource;
34 import org.springframework.util.Assert;
35 import org.springframework.util.PatternMatchUtils;
36
37 /**
38  * Factory for creating {@link SQLErrorCodes} based on the
39  * "databaseProductName" taken from the {@link java.sql.DatabaseMetaData}.
40  *
41  * <p>Returns <code>SQLErrorCodes</code> populated with vendor codes
42  * defined in a configuration file named "sql-error-codes.xml".
43  * Reads the default file in this package if not overridden by a file in
44  * the root of the class path (for example in the "/WEB-INF/classes" directory).
45  *
46  * @author Thomas Risberg
47  * @author Rod Johnson
48  * @author Juergen Hoeller
49  * @see java.sql.DatabaseMetaData#getDatabaseProductName()
50  */

51 public class SQLErrorCodesFactory {
52
53     /**
54      * The name of custom SQL error codes file, loading from the root
55      * of the class path (e.g. from the "/WEB-INF/classes" directory).
56      */

57     public static final String JavaDoc SQL_ERROR_CODE_OVERRIDE_PATH = "sql-error-codes.xml";
58
59     /**
60      * The name of default SQL error code files, loading from the class path.
61      */

62     public static final String JavaDoc SQL_ERROR_CODE_DEFAULT_PATH = "org/springframework/jdbc/support/sql-error-codes.xml";
63
64
65     private static final Log logger = LogFactory.getLog(SQLErrorCodesFactory.class);
66
67     /**
68      * Keep track of a single instance so we can return it to classes that request it.
69      */

70     private static final SQLErrorCodesFactory instance = new SQLErrorCodesFactory();
71
72
73     /**
74      * Return the singleton instance.
75      */

76     public static SQLErrorCodesFactory getInstance() {
77         return instance;
78     }
79
80
81     /**
82      * Map to hold error codes for all databases defined in the config file.
83      * Key is the database product name, value is the SQLErrorCodes instance.
84      */

85     private final Map JavaDoc errorCodesMap;
86
87     /**
88      * Map to cache the SQLErrorCodes instance per DataSource.
89      * Key is the DataSource, value is the SQLErrorCodes instance.
90      */

91     private final Map JavaDoc dataSourceCache = new HashMap JavaDoc(16);
92
93
94     /**
95      * Create a new instance of the {@link SQLErrorCodesFactory} class.
96      * <p>Not public to enforce Singleton design pattern. Would be private
97      * except to allow testing via overriding the
98      * {@link #loadResource(String)} method.
99      * <p><b>Do not subclass in application code.</b>
100      * @see #loadResource(String)
101      */

102     protected SQLErrorCodesFactory() {
103         Map JavaDoc errorCodes = null;
104
105         try {
106             DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
107             XmlBeanDefinitionReader bdr = new XmlBeanDefinitionReader(lbf);
108
109             // Load default SQL error codes.
110
Resource resource = loadResource(SQL_ERROR_CODE_DEFAULT_PATH);
111             if (resource != null && resource.exists()) {
112                 bdr.loadBeanDefinitions(resource);
113             }
114             else {
115                 logger.warn("Default sql-error-codes.xml not found (should be included in spring.jar)");
116             }
117
118             // Load custom SQL error codes, overriding defaults.
119
resource = loadResource(SQL_ERROR_CODE_OVERRIDE_PATH);
120             if (resource != null && resource.exists()) {
121                 bdr.loadBeanDefinitions(resource);
122                 logger.info("Found custom sql-error-codes.xml file at the root of the classpath");
123             }
124
125             // Check all beans of type SQLErrorCodes.
126
errorCodes = lbf.getBeansOfType(SQLErrorCodes.class, true, false);
127             if (logger.isInfoEnabled()) {
128                 logger.info("SQLErrorCodes loaded: " + errorCodes.keySet());
129             }
130         }
131         catch (BeansException ex) {
132             logger.warn("Error loading SQL error codes from config file", ex);
133             errorCodes = Collections.EMPTY_MAP;
134         }
135
136         this.errorCodesMap = errorCodes;
137     }
138     
139     /**
140      * Load the given resource from the class path.
141      * <p><b>Not to be overridden by application developers, who should obtain
142      * instances of this class from the static {@link #getInstance()} method.</b>
143      * <p>Protected for testability.
144      * @param path resource path; either a custom path or one of either
145      * {@link #SQL_ERROR_CODE_DEFAULT_PATH} or
146      * {@link #SQL_ERROR_CODE_OVERRIDE_PATH}.
147      * @return the resource, or <code>null</code> if the resource wasn't found
148      * @see #getInstance
149      */

150     protected Resource loadResource(String JavaDoc path) {
151         return new ClassPathResource(path);
152     }
153
154
155     /**
156      * Return the {@link SQLErrorCodes} instance for the given database.
157      * <p>No need for a database metadata lookup.
158      * @param dbName the database name (must not be <code>null</code> )
159      * @return the <code>SQLErrorCodes</code> instance for the given database
160      * @throws IllegalArgumentException if the supplied database name is <code>null</code>
161      */

162     public SQLErrorCodes getErrorCodes(String JavaDoc dbName) {
163         Assert.notNull(dbName, "Database product name must not be null");
164
165         SQLErrorCodes sec = (SQLErrorCodes) this.errorCodesMap.get(dbName);
166         if (sec == null) {
167             for (Iterator JavaDoc it = this.errorCodesMap.values().iterator(); it.hasNext();) {
168                 SQLErrorCodes candidate = (SQLErrorCodes) it.next();
169                 if (PatternMatchUtils.simpleMatch(candidate.getDatabaseProductNames(), dbName)) {
170                     sec = candidate;
171                     break;
172                 }
173             }
174         }
175         if (sec != null) {
176             if (logger.isDebugEnabled()) {
177                 logger.debug("SQL error codes for '" + dbName + "' found");
178             }
179             return sec;
180         }
181
182         // Could not find the database among the defined ones.
183
if (logger.isDebugEnabled()) {
184             logger.debug("SQL error codes for '" + dbName + "' not found");
185         }
186         return new SQLErrorCodes();
187     }
188
189     /**
190      * Return {@link SQLErrorCodes} for the given {@link DataSource},
191      * evaluating "databaseProductName" from the
192      * {@link java.sql.DatabaseMetaData}, or an empty error codes
193      * instance if no <code>SQLErrorCodes</code> were found.
194      * @param dataSource the <code>DataSource</code> identifying the database
195      * @see java.sql.DatabaseMetaData#getDatabaseProductName
196      */

197     public SQLErrorCodes getErrorCodes(DataSource JavaDoc dataSource) {
198         Assert.notNull(dataSource, "DataSource must not be null");
199         if (logger.isDebugEnabled()) {
200             logger.debug("Looking up default SQLErrorCodes for DataSource [" + dataSource + "]");
201         }
202
203         synchronized (this.dataSourceCache) {
204             // Let's avoid looking up database product info if we can.
205
SQLErrorCodes sec = (SQLErrorCodes) this.dataSourceCache.get(dataSource);
206             if (sec != null) {
207                 if (logger.isDebugEnabled()) {
208                     logger.debug("SQLErrorCodes found in cache for DataSource [" + dataSource + "]");
209                 }
210                 return sec;
211             }
212             // We could not find it - got to look it up.
213
try {
214                 String JavaDoc dbName = (String JavaDoc)
215                         JdbcUtils.extractDatabaseMetaData(dataSource, "getDatabaseProductName");
216                 if (dbName != null) {
217                     if (logger.isDebugEnabled()) {
218                         logger.debug("Database product name cached for DataSource [" + dataSource +
219                                 "]: name is '" + dbName + "'");
220                     }
221                     sec = getErrorCodes(dbName);
222                     this.dataSourceCache.put(dataSource, sec);
223                     return sec;
224                 }
225             }
226             catch (MetaDataAccessException ex) {
227                 logger.warn("Error while extracting database product name - falling back to empty error codes", ex);
228             }
229         }
230
231         // Fallback is to return an empty SQLErrorCodes instance.
232
return new SQLErrorCodes();
233     }
234
235 }
236
Popular Tags