Line | Hits | Source |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2006 TopCoder Inc., All Rights Reserved. | |
3 | */ | |
4 | package com.topcoder.document.index.persistence.impl.db; | |
5 | ||
6 | import com.topcoder.db.connectionfactory.ConfigurationException; | |
7 | import com.topcoder.db.connectionfactory.DBConnectionException; | |
8 | import com.topcoder.db.connectionfactory.DBConnectionFactory; | |
9 | import com.topcoder.db.connectionfactory.DBConnectionFactoryImpl; | |
10 | import com.topcoder.db.connectionfactory.UnknownConnectionException; | |
11 | import com.topcoder.document.index.persistence.IndexPersistence; | |
12 | import com.topcoder.document.index.persistence.IndexPersistenceException; | |
13 | import com.topcoder.document.index.persistence.impl.PersistenceConfigurationException; | |
14 | import com.topcoder.document.index.persistence.impl.Utils; | |
15 | ||
16 | import java.lang.reflect.Constructor; | |
17 | import java.sql.Connection; | |
18 | import java.sql.SQLException; | |
19 | import java.util.Arrays; | |
20 | ||
21 | ||
22 | /** | |
23 | * This is an abstraction of a typical data base driven persistence contract. This provides for getting a connection | |
24 | * from the DBConnectionFactory as well as specifying transaction control. This hides nicely the intricacies of | |
25 | * configuration that would be used by all derived classes. | |
26 | * <p/> | |
27 | * <b>Thread safety:</b>This class is no designed to be thread-safe. | |
28 | * | |
29 | * @author AleaActaEst, TCSDEVELOPER | |
30 | * @version 1.0 | |
31 | */ | |
32 | public abstract class AbstractDBIndexPersistence implements IndexPersistence { | |
33 | ||
34 | /** | |
35 | * This is the {@link com.topcoder.util.config.ConfigManager} lookup key used for reading the connection factory | |
36 | * class name from the ConfigManager. | |
37 | */ | |
38 | private static final String CONNECTION_FACTORY_CLASS_NAME_PROPERTY_NAME = "ConnectionFactoryClassName"; | |
39 | /** | |
40 | * This is the {@link com.topcoder.util.config.ConfigManager} lookup key used for reading the connection factory | |
41 | * namespace from the ConfigManager. | |
42 | */ | |
43 | private static final String CONNECTION_FACTORY_NAMESPACE_PROPERTY_NAME = "ConnectionFactoryNamespace"; | |
44 | /** | |
45 | * This is the {@link com.topcoder.util.config.ConfigManager} lookup key used for reading the {@link | |
46 | * #connectionName} from the ConfigManager. | |
47 | */ | |
48 | private static final String CONNECTION_NAME_PROPERTY_NAME = "ConnectionName"; | |
49 | ||
50 | /** | |
51 | * This field holds the connection factory from which we will obtain a pre-configured connection for our data base | |
52 | * access. This is initialized in one of the constructors. Cannot be <tt>null</tt>. | |
53 | */ | |
54 | private final DBConnectionFactory connectionFactory; | |
55 | ||
56 | /** | |
57 | * This field holds a connection name (an alias) that is used to fetch a connection instance from the connection | |
58 | * factory. This is initialized in one of the constructors and once initialized cannot be changed. Can be | |
59 | * <tt>null</tt> or an empty string, upon which it will try to use the default connection. | |
60 | */ | |
61 | private final String connectionName; | |
62 | ||
63 | /** | |
64 | * This constructor will populate the connectionFactory and connectionName information from configuration. | |
65 | * | |
66 | * @param namespace The namespace for connection-related properties. | |
67 | * | |
68 | * @throws IllegalArgumentException if namespace param is <tt>null</tt> or an empty (trim'd) String | |
69 | * @throws PersistenceConfigurationException | |
70 | * if there are configuration issues encountered | |
71 | */ | |
72 | 647 | protected AbstractDBIndexPersistence(final String namespace) throws PersistenceConfigurationException { |
73 | 647 | if (namespace == null) { |
74 | 3 | throw new IllegalArgumentException("The parameter named [namespace] was null."); |
75 | } | |
76 | 644 | if (namespace.trim().length() == 0) { |
77 | 2 | throw new IllegalArgumentException("The parameter named [namespace] was an empty String."); |
78 | } | |
79 | ||
80 | 642 | final String connectionFactoryClass = Utils.lookupOptionalStringFromConfigManager(namespace, |
81 | CONNECTION_FACTORY_CLASS_NAME_PROPERTY_NAME); | |
82 | 642 | final String connectionFactoryNamespace = Utils.lookupValidStringFromConfigManager(namespace, |
83 | CONNECTION_FACTORY_NAMESPACE_PROPERTY_NAME); | |
84 | 633 | if (connectionFactoryClass == null) { |
85 | try { | |
86 | 621 | connectionFactory = new DBConnectionFactoryImpl(connectionFactoryNamespace); |
87 | 3 | } catch (ConfigurationException e) { |
88 | 3 | throw new PersistenceConfigurationException( |
89 | "The DBConnectionFactory encountered a misconfiguration issue.", | |
90 | e); | |
91 | 0 | } catch (UnknownConnectionException e) { |
92 | 0 | throw new PersistenceConfigurationException( |
93 | "The DBConnectionFactory encountered a misconfiguration issue.", | |
94 | e); | |
95 | 618 | } |
96 | } else { | |
97 | 12 | connectionFactory = (DBConnectionFactory) createInstance(connectionFactoryClass, |
98 | 2 | new Class[]{String.class}, new Object[]{connectionFactoryNamespace}, DBConnectionFactory.class); |
99 | } | |
100 | 621 | connectionName = Utils.lookupOptionalStringFromConfigManager(namespace, CONNECTION_NAME_PROPERTY_NAME); |
101 | 621 | } |
102 | ||
103 | /** | |
104 | * This constructor will populate the connectionFactory and connectionName information from input parameters. | |
105 | * | |
106 | * @param connectionFactory DBConnectionFactory instance used to generate connections. | |
107 | * @param connectionName The name of the connection used for persistence operations. | |
108 | * | |
109 | * @throws IllegalArgumentException if connectionFactory is <tt>null</tt> | |
110 | */ | |
111 | 12 | protected AbstractDBIndexPersistence(final DBConnectionFactory connectionFactory, final String connectionName) { |
112 | 12 | if (connectionFactory == null) { |
113 | 3 | throw new IllegalArgumentException("The parameter named [connectionFactory] was null."); |
114 | } | |
115 | 9 | this.connectionFactory = connectionFactory; |
116 | // simplify empty String to null | |
117 | 9 | this.connectionName = connectionName != null |
118 | ? connectionName.trim().length() > 0 | |
119 | ? connectionName | |
120 | : null | |
121 | : null; | |
122 | 9 | } |
123 | ||
124 | /** | |
125 | * Returns a new connection to be used for persistence. This method will obtain the connection from the | |
126 | * connectionFactory using the connectionName alias. | |
127 | * | |
128 | * @return the transactional connection created | |
129 | * | |
130 | * @throws IndexPersistenceException If unable to obtain a Connection. | |
131 | */ | |
132 | protected Connection getConnection() throws IndexPersistenceException { | |
133 | try { | |
134 | 806 | final Connection connection = connectionName == null |
135 | ? connectionFactory.createConnection() | |
136 | : connectionFactory.createConnection(connectionName); | |
137 | 806 | connection.setAutoCommit(false); |
138 | 806 | return connection; |
139 | 0 | } catch (DBConnectionException e) { |
140 | 0 | throw new IndexPersistenceException( |
141 | "Connecting to the database (connection name was [" + connectionName + "]) failed.", e); | |
142 | 0 | } catch (SQLException e) { |
143 | 0 | throw new IndexPersistenceException("Initialization of connection parameters (autocommit) " |
144 | + "failed on the connection obtained from DBConnectionFactory (connection name was [" | |
145 | + connectionName + "]).", e); | |
146 | } | |
147 | } | |
148 | ||
149 | /** | |
150 | * Starts a transaction on the passed in connection. | |
151 | * | |
152 | * @param connection The Connection taking part in the transaction. | |
153 | * | |
154 | * @throws IndexPersistenceException If an error occurs during this process, or if the Connection does not support | |
155 | * transactions. | |
156 | * @throws IllegalArgumentException if connection is <tt>null</tt> | |
157 | */ | |
158 | protected void beginTransaction(final Connection connection) throws IndexPersistenceException { | |
159 | 154 | if (connection == null) { |
160 | 1 | throw new IllegalArgumentException("The parameter named [connection] was null."); |
161 | } | |
162 | ||
163 | // as stated by the designer this method currently does nothing | |
164 | 153 | } |
165 | ||
166 | /** | |
167 | * This method commits a transaction on the passed in connection. The connection is the closed. | |
168 | * | |
169 | * @param connection The Connection taking part in the transaction to be committed and closed | |
170 | * | |
171 | * @throws IndexPersistenceException If an error occurs during this process, or if the Connection does not support | |
172 | * transactions | |
173 | * @throws IllegalArgumentException if connection is <tt>null</tt> | |
174 | */ | |
175 | protected void commitTransaction(final Connection connection) throws IndexPersistenceException { | |
176 | 503 | if (connection == null) { |
177 | 1 | throw new IllegalArgumentException("The parameter named [connection] was null."); |
178 | } | |
179 | ||
180 | try { | |
181 | 502 | connection.commit(); |
182 | 1 | } catch (SQLException e) { |
183 | 1 | throw new IndexPersistenceException("Error while committing the connection [" + connection + "]", e); |
184 | } finally { | |
185 | 502 | DatabaseUtils.closeSilently(connection); |
186 | 501 | } |
187 | 501 | } |
188 | ||
189 | /** | |
190 | * This method rolls back the transaction on the passed connection. | |
191 | * | |
192 | * @param connection The Connection taking part in the transaction to be rolled back and closed | |
193 | * | |
194 | * @throws IndexPersistenceException If an error occurs during this process, or if the Connection does not support | |
195 | * transactions | |
196 | * @throws IllegalArgumentException if connection is <tt>null</tt> | |
197 | */ | |
198 | protected void rollbackTransaction(final Connection connection) throws IndexPersistenceException { | |
199 | 18 | if (connection == null) { |
200 | 1 | throw new IllegalArgumentException("The parameter named [connection] was null."); |
201 | } | |
202 | ||
203 | try { | |
204 | 17 | connection.rollback(); |
205 | 4 | } catch (SQLException e) { |
206 | 4 | throw new IndexPersistenceException("Error while rolling back the connection [" + connection + "]", e); |
207 | } finally { | |
208 | 17 | DatabaseUtils.closeSilently(connection); |
209 | 13 | } |
210 | 13 | } |
211 | ||
212 | /** | |
213 | * This method creates a new Object instance from the given class name. If the class could be loaded, a Constructor | |
214 | * that has the signature specified is looked up, which is then called specifying the given initargs. | |
215 | * | |
216 | * @param classname the name of the implementation class to be instantiated | |
217 | * @param constructorSignature the signature of the constructor to be called | |
218 | * @param initargs the constructor arguments to be used upon invocation of the constructor retrieved | |
219 | * @param expectedType the expected type of the instance created, will be checked after instantiation | |
220 | * | |
221 | * @return the instance created | |
222 | * | |
223 | * @throws PersistenceConfigurationException | |
224 | * the class specified cannot be loaded,no public constructor with the signature | |
225 | * specified does exist in the class or the constructor invocation throws an | |
226 | * exception | |
227 | * @throws IllegalArgumentException if any arg is <tt>null</tt>, or any String arg is an empty (trim'd) string or | |
228 | * the init args do not match the given signature | |
229 | */ | |
230 | private static Object createInstance(final String classname, | |
231 | final Class[] constructorSignature, | |
232 | final Object[] initargs, | |
233 | final Class expectedType) throws PersistenceConfigurationException { | |
234 | //arg checking is done in findConstructor | |
235 | 12 | final Constructor constructor = findConstructor(classname, initargs, expectedType, constructorSignature); |
236 | ||
237 | //create instance | |
238 | final Object instance; | |
239 | try { | |
240 | 3 | instance = constructor.newInstance(initargs); |
241 | 0 | } catch (Throwable t) { |
242 | // catch of Throwable is normally discouraged by the TC coding style, but | |
243 | // as a lot of Errors can arise from a reflection invocation and these | |
244 | // errors don't need to be let pass through this catch clause (as they | |
245 | // definitely belong to the reflective invocation and not the normal program | |
246 | // execution) all Throwables are caught here and | |
247 | // wrapped into a ConfigurationException. | |
248 | 0 | throw new PersistenceConfigurationException("the implementation class [" + classname |
249 | + "] could not be instantiated, constructor invocation failed.", t); | |
250 | 3 | } |
251 | 3 | return instance; |
252 | } | |
253 | ||
254 | /** | |
255 | * This method loads the class from the given class name. If the class could be loaded, a Constructor that has the | |
256 | * signature specified is looked up. | |
257 | * | |
258 | * @param classname the name of the implementation class to be loaded | |
259 | * @param constructorSignature the signature of the constructor to be fond | |
260 | * @param initargs the constructor arguments to be used upon invocation of the constructor retrieved | |
261 | * (for checking conformance with given signature) | |
262 | * @param expectedType the expected type of the class loaded, if not assignable, ConfigurationException will | |
263 | * be thrown | |
264 | * | |
265 | * @return the Constructor found | |
266 | * | |
267 | * @throws PersistenceConfigurationException | |
268 | * the class specified cannot be loaded,no public constructor with the signature | |
269 | * specified does exist in the class or the constructor invocation throws an | |
270 | * exception | |
271 | * @throws IllegalArgumentException if any arg is <tt>null</tt>, or any String arg is an empty (trim'd) string or | |
272 | * the init args do not match the given signature or the constructor signature | |
273 | * contains a <tt>null</tt> value | |
274 | */ | |
275 | private static Constructor findConstructor(final String classname, final Object[] initargs, | |
276 | final Class expectedType, | |
277 | final Class[] constructorSignature) | |
278 | throws PersistenceConfigurationException { | |
279 | 12 | if (classname == null) { |
280 | 0 | throw new IllegalArgumentException("The parameter named [classname] was null."); |
281 | } | |
282 | 12 | if (classname.trim().length() == 0) { |
283 | 0 | throw new IllegalArgumentException("The parameter named [classname] was an empty String."); |
284 | } | |
285 | 12 | if (initargs == null) { |
286 | 0 | throw new IllegalArgumentException("The parameter named [initargs] was null."); |
287 | } | |
288 | 12 | if (expectedType == null) { |
289 | 0 | throw new IllegalArgumentException("The parameter named [expectedType] was null."); |
290 | } | |
291 | 12 | if (constructorSignature == null) { |
292 | 0 | throw new IllegalArgumentException("The parameter named [constructorSignature] was null."); |
293 | } | |
294 | 12 | checkArgsMatchSignature(constructorSignature, initargs); |
295 | ||
296 | //lookup and load instance implementation class | |
297 | final Class implClass; | |
298 | try { | |
299 | 12 | implClass = Class.forName(classname); |
300 | 3 | } catch (Throwable t1) { |
301 | // catch of Throwable is normally discouraged by the TC coding style, but | |
302 | // as a lot of Errors can arise from a class load and these | |
303 | // errors don't need to be let pass through this catch clause (as they | |
304 | // definitely belong to the class load and not the normal program | |
305 | // execution) all Throwables are caught here and | |
306 | // wrapped into a ConfigurationException. | |
307 | 3 | throw new PersistenceConfigurationException("Unable to load implementation class [" + classname + "].", t1); |
308 | 9 | } |
309 | ||
310 | //check if class matches expected type | |
311 | 9 | if (!expectedType.isAssignableFrom(implClass)) { |
312 | 6 | throw new PersistenceConfigurationException( |
313 | "the implementation class [" + classname + "] is not of type [" + expectedType.getName() + "]."); | |
314 | } | |
315 | ||
316 | //find constructor | |
317 | final Constructor constructor; | |
318 | try { | |
319 | 3 | constructor = implClass.getConstructor(constructorSignature); |
320 | 0 | } catch (NoSuchMethodException e) { |
321 | 0 | throw new PersistenceConfigurationException("The implementation class [" + implClass .getName() |
322 | + "] did not have a constructor with signature [" + Arrays.asList(constructorSignature).toString() | |
323 | + "].", e); | |
324 | ||
325 | 3 | } |
326 | 3 | return constructor; |
327 | } | |
328 | ||
329 | /** | |
330 | * This method checks whether each element of the initargs is assignable to the type specified in the equivalent | |
331 | * constructorSignature element, both arrays have same size and constructorSignature does not contain <tt>null</tt> | |
332 | * values. | |
333 | * | |
334 | * @param constructorSignature the signature to check for match with initargs | |
335 | * @param initargs the initargs to check for match with constructorSignature | |
336 | * | |
337 | * @throws IllegalArgumentException in case any of the checks fails and thus the given initargs cannot be used as | |
338 | * args for a constructor of the given signature | |
339 | */ | |
340 | private static void checkArgsMatchSignature(final Class[] constructorSignature, final Object[] initargs) { | |
341 | 12 | if (initargs.length != constructorSignature.length) { |
342 | 0 | throw new IllegalArgumentException("The given constructor signature ([" + constructorSignature.length |
343 | + "] args) and the given initargs ([" + initargs.length + "] args) have different length."); | |
344 | } | |
345 | 24 | for (int i = 0; i < constructorSignature.length; i++) { |
346 | 12 | final Class argType = constructorSignature[i]; |
347 | 12 | final Object argValue = initargs[i]; |
348 | 12 | if (argType == null) { |
349 | 0 | throw new IllegalArgumentException( |
350 | "The parameter named [constructorSignature] contained a null value."); | |
351 | } | |
352 | ||
353 | // a null argument does match any type | |
354 | 12 | if (argValue != null) { |
355 | // check if actual arg matches declared arg type of constructor signature | |
356 | 12 | if (!argType.isAssignableFrom(argValue.getClass())) { |
357 | 0 | throw new IllegalArgumentException("The given initArg element at position [" + i + "] is of type [" |
358 | + argValue.getClass().getName() | |
359 | + "] which does not match the argument type specified in the constructor signature [" | |
360 | + argType.getName() + "] for that argument."); | |
361 | } | |
362 | } | |
363 | } | |
364 | 12 | } |
365 | ||
366 | } |
this report was generated by version 1.0.5 of jcoverage. |
copyright © 2003, jcoverage ltd. all rights reserved. |