View Javadoc

1   package com.explosion.expfmodules.rdbmsconn.connect;
2   
3   /*
4    * =============================================================================
5    * 
6    * Copyright 2004 Stephen Cowx
7    * 
8    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
9    * use this file except in compliance with the License. You may obtain a copy of
10   * the License at
11   * 
12   * http://www.apache.org/licenses/LICENSE-2.0
13   * 
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   * 
20   * =============================================================================
21   */
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.net.URL;
26  import java.sql.Connection;
27  import java.sql.Driver;
28  import java.sql.DriverManager;
29  import java.sql.SQLException;
30  import java.util.ArrayList;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Properties;
34  import java.util.StringTokenizer;
35  
36  import org.apache.log4j.LogManager;
37  import org.apache.log4j.Logger;
38  
39  import com.explosion.expfmodules.rdbmsconn.DriverDescriptorManager;
40  import com.explosion.expfmodules.rdbmsconn.DriverDescriptorPreference;
41  import com.explosion.expfmodules.rdbmsconn.RdbmsConnConstants;
42  import com.explosion.utilities.PropertiesUtils;
43  import com.explosion.utilities.classes.StandardClassLoader;
44  import com.explosion.utilities.exception.EnhancedException;
45  import com.explosion.utilities.exception.ExceptionManagerFactory;
46  import com.explosion.utilities.preferences.Preference;
47  import com.explosion.utilities.preferences.groups.PreferenceGroup;
48  
49  /***
50   * This class creates database connections to specific databases. Its intended
51   * use is toprovide a Connection object back to the Caller whch the caller can
52   * then use to make jdbc calls.
53   *  
54   */
55  
56  public class ConnectionFactory
57  {
58  
59      private static Logger log = LogManager.getLogger(ConnectionFactory.class);
60  
61      /***
62       * This method will create a connection to a database using a standard
63       * propertiesentry
64       */
65      public Connection createConnection(String connectionName, String propertiesFile) throws Exception
66      {
67          Properties props = PropertiesUtils.loadProperties(propertiesFile);
68          return (createConnection(connectionName, props));
69      }
70      
71      /***
72       * This method prepares the connection for use.
73       * Sets autocommit and other values
74       * @param conn
75       * @throws SQLException 
76       */
77      private void prepareConnection(Connection conn) throws SQLException
78      {
79          conn.setAutoCommit(false);
80      }
81  
82      /***
83       * This method will create a connection to a database using a standard
84       * propertiesentry
85       */
86      public Connection createConnection(String connectionName, Properties props) throws Exception
87      {
88          Connection connection = null;
89          String url = null;
90          try
91          {
92              String driver = props.getProperty(connectionName + "." + "driver");
93              if (driver == null)
94                  throw new Exception("Database property 'driver' not found for connection '" + connectionName + "'.");
95  
96              url = props.getProperty(connectionName + "." + "url");
97              if (url == null)
98                  throw new Exception("Database property 'url' not found for connection '" + connectionName + "'.");
99  
100             String user = props.getProperty(connectionName + "." + "user");
101             if (user == null)
102                 throw new Exception("Database property 'user' not found for connection '" + connectionName + "'.");
103 
104             String password = props.getProperty(connectionName + "." + "password");
105             if (password == null)
106                 throw new Exception("Database property 'password' not found for connection '" + connectionName + "'.");
107 
108             log.debug("Found properties Url: "+url+", user:"+user+", password:"+password);
109             
110             String classpath = props.getProperty(connectionName + "." + "classpath");
111             if (classpath != null)
112             {
113                 log.debug("Using independant based connection with classpath " + classpath);
114                 List list = new ArrayList();
115                 StringTokenizer tokenizer = new StringTokenizer(classpath, ",");
116                 while (tokenizer.hasMoreTokens())
117                 {
118                     list.add(new File(tokenizer.nextToken()));
119                 }
120                 connection = openDefinedConnection(driver, list, url, user, password);
121             }
122             else
123             {
124                 connection = openEmbeddedConnection(driver, url, user, password);
125             }
126             
127             /* Returns the connection */
128             return connection;
129         }
130         catch (Exception e)
131         {
132             throw new UnableToConnectException("Unable to connect to database '" + url + "'.  Reason:: ", e);
133         }
134         
135     }
136 
137     /***
138      * This method will create a connection to a database using a
139      * ConnectionDescriptor
140      * 
141      * @param connectionDescriptor
142      * @return @throws Exception
143      */
144     public Connection createConnection(PreferenceGroup connectionDescriptor, DriverDescriptorManager manager) throws Exception
145     {
146         PreferenceGroup driverDescriptor = manager.getGroup((String) connectionDescriptor.getPreferenceValue(RdbmsConnConstants.CD_DRIVER));
147         log.debug("Using descriptor " + driverDescriptor + " to attempt to log on to database.");
148         if (driverDescriptor == null)
149             throw new Exception("Driver '" + (String) connectionDescriptor.getPreferenceValue(RdbmsConnConstants.CD_DRIVER) + "' is not defined.");
150 
151         return createConnection(connectionDescriptor, driverDescriptor);
152     }
153 
154     /***
155      * This method will create a connection to a database using a
156      * ConnectionDescriptor and a driverDescriptor
157      * 
158      * @param connectionDescriptor
159      * @param driverDescriptor
160      * @return @throws Exception
161      */
162     private Connection createConnection(PreferenceGroup connectionDescriptor, PreferenceGroup driverDescriptor) throws Exception
163     {
164         try
165         {
166             String driverName = (String) driverDescriptor.getPreferenceValue(RdbmsConnConstants.DRVR_NAME);
167             boolean isEmbedded = ((Boolean) driverDescriptor.getPreferenceValue(RdbmsConnConstants.DRVR_EMBEDDED)).booleanValue();
168             DriverDescriptorPreference driverDescriptorPreference = (DriverDescriptorPreference) driverDescriptor.getPreference(RdbmsConnConstants.DRVR_NAME);
169             Preference locations = (Preference) driverDescriptor.getPreference(RdbmsConnConstants.DRVR_LOCATION);
170 
171             if (driverName == null)
172                 throw new Exception("Insufficient information specified for Driver '" + (String) connectionDescriptor.getPreferenceValue(RdbmsConnConstants.CD_DRIVER)
173                         + "' to be able to make a connection to a database.");
174 
175             String url = (String) connectionDescriptor.getPreferenceValue(RdbmsConnConstants.CD_URL);
176             String user = (String) connectionDescriptor.getPreferenceValue(RdbmsConnConstants.CD_USER);
177             String password = (String) connectionDescriptor.getPreferenceValue(RdbmsConnConstants.CD_PASS);
178 
179             if (isEmbedded)
180             {
181                 log.debug("Using embedded driver: " + driverName);
182                 return openEmbeddedConnection(driverName, url, user, password);
183             }
184             else
185             {
186                 log.debug("Using configured driver: " + driverName + ".");
187                 return openDefinedConnection(driverName, locations.getValues(), url, user, password, driverDescriptorPreference);
188             }
189         }
190         catch (Exception e)
191         {
192             throw new UnableToConnectException("Unable to establish connection. ", e);
193         }
194     }
195 
196     /***
197      * Connects to a database using the Embedded DriverManager rather than an
198      * independant Driver
199      * 
200      * @param driverName
201      * @param url
202      * @param user
203      * @param password
204      * @return @throws SQLException
205      * @throws InstantiationException
206      * @throws IllegalAccessException
207      * @throws ClassNotFoundException
208      */
209     public Connection openEmbeddedConnection(String driverName, String url, String user, String password) throws SQLException, InstantiationException, IllegalAccessException,
210             ClassNotFoundException, UnableToConnectException
211     {
212         log.debug("Using url '" + url + "' and user '" + user + "'.");
213         DriverManager.registerDriver((Driver) Class.forName(driverName).newInstance());
214         Connection conn = null;
215         conn = DriverManager.getConnection(url, user, password);
216         
217         if (conn == null)
218         {
219             throw new UnableToConnectException("Unable to connect to database.  Database returned no errors but connection failed.");
220         }
221         
222         /* Prepare the connection */
223         prepareConnection(conn);
224         
225         return conn;
226     }
227 
228     /***
229      * Connects to a database using the an independent Driver
230      * 
231      * @param driverName
232      * @param classpath
233      * @param url
234      * @param user
235      * @param password
236      * @return @throws SQLException
237      * @throws InstantiationException
238      * @throws IllegalAccessException
239      * @throws ClassNotFoundException
240      */
241     private Connection openDefinedConnection(String driverName, List classPath, String url, String user, String password) throws SQLException, IllegalArgumentException, EnhancedException,
242             IOException, InstantiationException, IllegalAccessException, ClassNotFoundException
243     {
244         return openDefinedConnection(driverName, classPath, url, user, password, null);
245     }
246 
247     /***
248      * Connects to a database using the an independent Driver
249      * 
250      * @param driverName
251      * @param classpath
252      * @param url
253      * @param user
254      * @param password
255      * @param driverDescriptorPreference
256      * @return @throws SQLException
257      * @throws InstantiationException
258      * @throws IllegalAccessException
259      * @throws ClassNotFoundException
260      */
261     private Connection openDefinedConnection(String driverName, List classPath, String url, String user, String password, DriverDescriptorPreference driverDescriptorPreference)
262             throws SQLException, IllegalArgumentException, EnhancedException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException
263     {
264         log.debug("Using url '" + url + "' and user '" + user + "'.");
265         java.util.Properties info = new java.util.Properties();
266         if (user != null)
267         {
268             info.put("user", user);
269         }
270 
271         if (password != null)
272         {
273             info.put("password", password);
274         }
275 
276         Driver driver = null;
277         if (driverDescriptorPreference != null)
278         {
279             /*
280              * First see if we have loaded this driver before. If we have then
281              * we should use that instaed of loading it all over again. There is
282              * no reason that I can see to use a different class loaded by a
283              * different driver each time. The connections should be unique
284              * anyway.
285              * 
286              * In the case of the DB2 JDBC driver there appears to be a strong
287              * case for reusing the Driver object. It obtains a handle to a
288              * particular dll (in windows anyway) and wont let you load the
289              * driver through a different class loader.
290              */
291             driver = driverDescriptorPreference.getDriver();
292 
293             if (driver == null)
294             {
295                 driver = getDriver(driverName, classPath);
296                 driverDescriptorPreference.setDriver(driver);
297             }
298         }
299         else
300         {
301             driver = getDriver(driverName, classPath);
302         }
303         
304         log.debug("Driver found and instantiated " + driver + ".  About to call driver.connect()");
305 
306         Connection conn = null;
307         try
308         {
309             conn = driver.connect(url, info);
310             if (conn == null)
311             {
312                throw new UnableToConnectException("Unable to connect to database.  Database returned no errors but connection failed.");
313             }
314         }
315         catch (Throwable e)
316         {
317             ExceptionManagerFactory.getExceptionManager().manageException(e, "Error while connecting to database.");
318         }
319         
320         /* Prepare the connection */
321         prepareConnection(conn);
322         
323         return conn;
324     }
325 
326     /***
327      * Loads an instance of a Driver from a jarfile
328      * 
329      * @param driverName
330      * @param jarFile
331      * @return
332      */
333     private Driver getDriver(String driverName, List classPathFiles) throws EnhancedException, IllegalArgumentException, IOException, InstantiationException, IllegalAccessException,
334             ClassNotFoundException
335     {
336         /* Manually specified driver */
337         if (classPathFiles != null)
338         {
339             if (classPathFiles.size() > 0)
340             {
341 
342                 String[] repo = new String[classPathFiles.size()];
343                 int index = 0;
344                 for (Iterator it = classPathFiles.iterator(); it.hasNext();)
345                 {
346                     File file = (File) it.next();
347                     URL url = file.toURL();
348                     String urlString = url.getProtocol() + ":" + url.getPath();
349                     repo[index] = urlString;
350                     index++;
351                 }
352 
353                 StandardClassLoader loader = new StandardClassLoader(repo);
354 
355                 log.debug("Instantiating driver " + driverName);
356                 return (Driver) Class.forName(driverName, true, loader).newInstance();
357             }
358             else
359             {
360                 throw new DriverNotSpecifiedException("Unable to load driver for connection because no driver files have been specified.");
361             }
362         }
363         else
364         {
365             throw new InvalidDriverConfigurationException("Unable to load driver for connection because no file containing a driver has been specified.");
366         }
367     }
368 
369 }